Description
Describe the issue
StatefulWidget classes sometimes adds event listeners but fails to remove them. This results in memory leaks if the valueListenable lifecycle is significantly longer than the widget. These bugs are hard for users to catch as everything seems fine they are just leaking memory. These memory leaks could also be detected by runtime tools but is preferable to catch them with static analysis if we can do it without false positives.
To start with, we should focus on only Listenable
(and subclasses) but this also applies to streams and many other listenable objects in Dart.
Every listener added manually needs to be removed typically in the dispose method.
If listeners are added in didUpdateWidget
or updateDependencies
then they also need to be removed from those methods as otherwise widgets end up with multiple listeners.
To Reproduce
void initState() {
super.initState();
widget.valueListenable.addListener(myListener);
}
didUpdateWidget(MyWidget oldWidget) {
if (oldWidget.valueListenable != widget.valueListenable) {
widget.valueListenable.addListener(myListener);
oldWidget.valueListenable.removeListener(myListener); // error if this line is removed.
}
}
void dispose() {
widget.valueListenable.removeListener(myListener); // Error if this is omitted.
super.dispose();
}
Expected behavior
Expect warnings for cases where a State object is never removing listeners it is adding.
Additional context
Memory leaks can be significant problem for Flutter applications that manually add and remove listeners as it is easy to get the lifecycle wrong as it is manual.