Alexey Inkin
Alexey Inkin

Reputation: 2039

Flutter showDialog completes before the dialog is destroyed

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

Answers (1)

Sanket Vekariya
Sanket Vekariya

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

Related Questions