Flutter. New Disposer widget
1. Introduction
Almost every Flutter app has a form with text fields.
The text field requires TextEditingConroller
.
TextEditingController
has a dispose()
method. Therefore we need a StatefulWidget
just to dispose of a TextEditingController
.
Stateful widgets are verbose, hardly readable, not performance-friendly, and violate several software development principles.
2. An example of resources disposal using StatefulWidget
class Test extends StatefulWidget {
@override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
TextEditingController controller;
FocusNode focusNode;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Disposer Example View'),
centerTitle: true,
),
body: Center(
child: Container(
child: TextField(
focusNode: focusNode,
controller: controller,
),
),
),
);
}
@override
void initState() {
controller = TextEditingController();
focusNode = FocusNode();
super.initState();
}
@override
void dispose() {
controller.dispose();
focusNode.dispose();
super.dispose();
}
}
It would be much better if we just use StatelessWidget
instead.
Here are several questions on Stackoverflow which raise the same problem:
The problem, obviously, is not limited to the TextEditingController
, it is also animation controllers, subscriptions to steams, timers, and any Dart objects with the dispose()
method.
All of them require StatefulWidgets
, but we would rather use StatelessWidget
instead.
But we cannot since StatelessWidget
doesn’t have the dispose()
method.
And here the Disposer
widget comes as a savior.
3. An example of resources disposal using StatelessWidget with a Disposer
class DisposerExampleView extends StatelessWidget {
DisposerExampleView({super.key});
TextEditingController controller = TextEditingController();
FocusNode focusNode = FocusNode();
void dispose() { //here
controller.dispose();
focusNode.dispose();
print('controller and focusNode were disposed');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Disposer Example View'),
centerTitle: true,
),
body: Center(
child: Column(
children: [
TextField(
focusNode: focusNode,
controller: controller,
),
Disposer(dispose: dispose), //and here
],
)),
);
}
}
We just wrote the dispose
method and supplied it to Disposer
widget’s dispose
property.
And, voila, we don’t need StatefulWidget anymore. Our code became shorter and more readable.
So, where does the Disposer
widget come from?
It comes from nowhere, we need to write it ourselves. But it is really easy to do.
4. Disposer widget implementation
class Disposer extends StatefulWidget {
final void Function() dispose;
const Disposer({super.key, required this.dispose});
@override
DisposerState createState() {
return DisposerState();
}
}
class DisposerState extends State<Disposer> {
@override
Widget build(BuildContext context) {
return SizedBox.shrink();
}
@override
void dispose() {
widget.dispose();
super.dispose();
}
}
We can now include it in our project template somewhere in common/widgets
and use it in all projects. I am thinking of publishing it as a package. If you would like to have a package like this, please comment.
5. Conclusion
Problem: Many Flutter apps require
TextEditingController
and other objects that need disposal. This often forces developers to useStatefulWidget
, which can be verbose and less performant.Proposed solution: A custom
Disposer
widget that allows usingStatelessWidget
while still properly disposing of resources.Benefits of the solution:
1) Shorter, more readable code
2) Ability to useStatelessWidget
instead ofStatefulWidget
Subscribe to my newsletter
Read articles from Yuriy Novikov directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by