Flutter - TextEditingController 在处理后使用

Flutter - TextEditingController used after being disposed

我做了以下简单的 class 以允许输入字符串:

import 'package:flutter/material.dart';

class MyDialog {
  late TextEditingController _controller;

  Future<String> showMyDialog(BuildContext context) async {
    _controller = TextEditingController();

    final result = await showDialog(
      context: context,
      builder: (BuildContext context) => _buildMyDialog(context),
    );

    _controller.dispose();
    return result;
  }

  Widget _buildMyDialog(BuildContext context) {
    return AlertDialog(
      title: const Text('Enter string: '),
      content: TextField(
        autofocus: true,
        controller: _controller,
      ),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.of(context).pop(_controller.text);
          },
          child: const Text('OK'),
        ),
      ],
    );
  }
}

我这样称呼它:

final dialog = MyDialog();
final result = await dialog.showMyDialog(context);

不幸的是,我收到以下错误:

A TextEditingController was used after being disposed.

为什么会发生以及如何解决?毕竟我把控制器扔掉后就不会在任何地方使用了。

showMyDialog上每次都更新TextEditingController,我们可以评论_controller.dispose();。此外,有可能在此方法上获得 null,因此使用

会更好
Future<String?> showMyDialog(...){...}

我们可以做的另一件事是让它可以为空

class MyDialog {
  TextEditingController? _controller;

  Future<String?> showMyDialog(BuildContext context) async {
    _controller = TextEditingController();

    final String? result = await showDialog(
      context: context,
      builder: (BuildContext context) => _buildMyDialog(context),
    );
    _controller = null;
    return result;
  }
//....

你可以查看一下

我对 Yeasin 的回答 (在撰写本文时) 的担忧是你没有在控制器上调用 dispose() 而且我不是 100% 确定什么是无效的在这方面,实例确实如此。它可能会避免错误,但可能存在内存泄漏或其他问题(假设)。

我建议的另一种方法是将您的 AlertDialog 变成 StatefulWidget,这样您就可以利用 Widgets 的内置 dispose() 方法。这将使您避免对控制器进行微观管理。相反,您在包含控制器的 Widget 的 dispose 方法中调用控制器的 dispose 方法。

下面是您的代码利用 Flutter 的这一功能的样子,它不会改变您调用代码以显示对话框的方式:

class MyDialog {
    
    Future<String> showMyDialog(BuildContext context) async {
      final result = await showDialog(
        context: context,
        builder: (BuildContext context) => MyAlertDialog(),
      );
      return result;
    }
    
}

class MyAlertDialog extends StatefulWidget {
    @override
    MyAlertDialog createState() => MyAlertDialogState();
} 

class MyAlertDialogState extends State<MyAlertDialog> {
    
        // for this example, it's safe to instantiate the controller inline
        TextEditingController _controller = new TextEditingController();
        
        @override
        void dispose() {
            // attempt to dispose controller when Widget is disposed
            try { _controller.dispose(); } catch (e) {}
        }
        
        Widget build(BuildContext context) {
            return AlertDialog(
                title: const Text('Enter string: '),
                content: TextField(
                    autofocus: true,
                    controller: _controller,
                ),
                actions: [
                    TextButton(
                        onPressed: () {
                            Navigator.of(context).pop(_controller.text);
                        },
                        child: const Text('OK'),
                    ),
                ],
            );
        }
    
}