Reputation: 2039
I call showDialog
to construct an AlertDialog
that receives TextEditingController
. I await
for the dialog to close, and then I try to dispose
the controller. But I get an exception:
A TextEditingController was used after being disposed.
Why is this happening? And how do I wait until the dialog is destroyed for sure?
My code:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _onPressed(context),
child: Text('Show'),
),
),
);
}
void _onPressed(BuildContext context) async {
final controller = TextEditingController();
await showDialog(
context: context,
builder: (_) => MyDialog(controller),
);
controller.dispose();
}
}
class MyDialog extends StatelessWidget {
final TextEditingController controller;
MyDialog(this.controller);
@override
Widget build(BuildContext context) {
return AlertDialog(
content: TextField(
controller: controller,
),
);
}
}
My next attempt was to wrap dispose()
into this:
SchedulerBinding.instance!.addPostFrameCallback((_) {
controller.dispose();
});
Still the error persists.
This error is not visible to the user and could be ignored. Also the controller is not that expensive to leave undisposed. Finally I can use StatefulWidget
for the dialog content, create the controller there and dispose it in the widget's dispose()
.
But I am mostly interested in the framework design that leads to this as my case is actually more elaborate.
So why is this happening? And how do I wait until the dialog is destroyed for sure?
Upvotes: 2
Views: 1530
Reputation: 2956
The issue is because the controller is disposed of even the view is available.
Improvement Suggestions
You can create a single controller instead of multiple for memory efficiency.
For that, you need to clear the controller every time the dialog opens.
Code to Change
class MyHomePage extends StatelessWidget {
TextEditingController controller = TextEditingController();// <= Add here
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _onPressed(context),
child: Text('Show'),
),
),
);
}
void _onPressed(BuildContext context) {
controller.clear(); //<= Add here
showDialog(
context: context,
builder: (_) => MyDialog(controller),
);
}
}
Upvotes: 1