Skip to content

dispose_fields

v0.4.0 Warning Fix Resource Management

Flags instance fields in State subclasses whose type has a dispose(), close(), or cancel() method but the field is not cleaned up in the widget’s dispose() method. Common types include TextEditingController, FocusNode, AnimationController, StreamController, StreamSubscription, and Timer.

Disposable resources that are not cleaned up cause memory leaks. A TextEditingController that is never disposed keeps its listeners and internal state alive indefinitely. This rule checks that every field with a cleanup method has a matching call in dispose(), catching missing or incomplete cleanup.

See also: State.dispose()

class _BadState extends State<BadWidget> {
final _textController = TextEditingController();
final _focusNode = FocusNode();
final _streamController = StreamController<int>();
// No dispose() method -- all fields leak
@override
Widget build(BuildContext context) => const SizedBox();
}
class _IncompleteState extends State<IncompleteWidget> {
final _controller1 = TextEditingController();
final _controller2 = TextEditingController();
@override
void dispose() {
_controller1.dispose();
// _controller2 is missing!
super.dispose();
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _GoodState extends State<GoodWidget> {
final _textController = TextEditingController();
final _focusNode = FocusNode();
@override
void dispose() {
_textController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => const SizedBox();
}
class _StreamState extends State<StreamWidget> {
final _streamController = StreamController<int>();
@override
void dispose() {
_streamController.close();
super.dispose();
}
@override
Widget build(BuildContext context) => const SizedBox();
}

To disable this rule:

plugins:
many_lints:
diagnostics:
dispose_fields: false