Skip to main content

avoid-unremovable-callbacks-in-listeners

effort: 4m
configurable
teams+

Warns when an addListener invocation receives a callback that cannot be unregistered.

Since no function expression (() => ... or () { ... }) is equal to any other function expression, passing it to addListener creates a callback that cannot be unregistered via removeListener and thus leads to a memory leak. Same applies to extension method tear-offs as they are never equal to each other (including methods with the same signature).

To fix that, assign the function expression to a field or variable and use that field or variable in both addListener and removeListener.

⚙️ Config

Set additional-methods (default is empty) to include additional methods on top of addListener and removeListener (this option supports methods that accept a list of callbacks as well).

analysis_options.yaml
dcm:
rules:
- avoid-unremovable-callbacks-in-listeners:
additional-methods:
- addAll
- myCustomAddListener

Example

❌ Bad:

class SomeClass {
final _someListener = Listener();

void work() {
// LINT: Avoid passing function expressions as callbacks since they are never equal to each other and can't be unregistered.
_someListener.addListener(() {});

// LINT: Avoid passing function expressions as callbacks since they are never equal to each other and can't be unregistered
_someListener.addListener(() => null);
}
}

✅ Good:

class SomeClass {
final _someListener = Listener();

void work() {
_someListener.addListener(listener);
}

void listener() => print('empty');
}