avoid_unremovable_callbacks_in_listeners
This rule flags a closure literal passed to addListener. removeListener matches by identity, and a closure creates a new object each time it is evaluated — so the listener can never be removed.
Why use this rule
Section titled “Why use this rule”removeListener(theClosure) compares object identity. A closure literal written at the call site is a different object from any closure you could later pass, so the removal silently does nothing and the listener stays registered.
Two consequences follow. The listener holds its captured scope — usually the whole State — alive for as long as the notifier lives, which is a genuine leak. And it keeps firing after disposal, so a setState inside it runs against a disposed element.
This pairs with always_remove_listener: that rule checks that a removal exists, this one checks that the removal can actually work.
See also: Flutter: ChangeNotifier.removeListener
controller.addListener(() => setState(() {})); // can never be removedGive the callback a stable identity:
void _onChange() => setState(() {});
@overridevoid initState() { super.initState(); controller.addListener(_onChange);}
@overridevoid dispose() { controller.removeListener(_onChange); super.dispose();}Known limitations
Section titled “Known limitations”Only addListener and addStatusListener are recognised by default; a project wrapper can be added with additional_methods.
A registration with more than one argument is skipped, since this rule is about the add/remove pair specifically.
Turning this rule off
Section titled “Turning this rule off”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated.
To turn it off:
rules: avoid_unremovable_callbacks_in_listeners: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”many_lints: rules: avoid_unremovable_callbacks_in_listeners: additional_methods: [addObserver]rules: avoid_unremovable_callbacks_in_listeners: additional_methods: [addObserver]| Option | Type | Default | Description |
|---|---|---|---|
additional_methods |
list of strings | [] |
Extra registration methods whose counterpart removes by identity |
Related rules
Section titled “Related rules”always_remove_listener— Ensure every addListener() has a matching removeListener() in dispose().dispose_fields— Ensure State fields with disposal methods are cleaned up in dispose().dispose_provided_instances— Ensure disposable instances in Riverpod providers are cleaned up with ref.onDispose.avoid_late_final_reassignment— Flag alate finalfield assigned twice on one path.