always_remove_listener
v0.4.0 Warning Resource Management
Flags addListener() calls in State lifecycle methods (initState, didUpdateWidget, didChangeDependencies) that do not have a matching removeListener() call in dispose(). Missing removal causes memory leaks when the Listenable outlives the widget.
Why use this rule
Section titled “Why use this rule”Every addListener() on a ChangeNotifier, ValueNotifier, or AnimationController creates a strong reference to the callback. If the listener is not removed in dispose(), the callback (and everything it captures) stays in memory even after the widget is unmounted. This rule ensures every add has a matching remove with the same target and callback.
See also: ChangeNotifier | removeListener
class _BadState extends State<BadWidget> { final ValueNotifier<int> _counter = ValueNotifier(0);
@override void initState() { super.initState(); _counter.addListener(_onChanged); // No matching removeListener }
void _onChanged() => setState(() {});
@override Widget build(BuildContext context) => const SizedBox();}class _GoodState extends State<GoodWidget> { final ValueNotifier<int> _counter = ValueNotifier(0);
@override void initState() { super.initState(); _counter.addListener(_onChanged); }
@override void dispose() { _counter.removeListener(_onChanged); super.dispose(); }
void _onChanged() => setState(() {});
@override Widget build(BuildContext context) => const SizedBox();}Configuration
Section titled “Configuration”To disable this rule:
plugins: many_lints: diagnostics: always_remove_listener: false