(颤振)如何在不按“完成”的情况下自动侦听TextField中的更改?

时间:2019-03-18 15:38:21

标签: dart flutter

我在对话框中打印来自TextField的输入(TextField和输出都在AlertDialog中)。仅当我在键盘上按“完成”时,输出才会更新。如果我不按“完成”,它将继续显示旧的输出。

这是我的源代码:

Future<void> widePopUpCustom() async {
  return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
            title: Text('Enter Number'),
            content: SingleChildScrollView(
                child: ListBody(children: <Widget>[
              TextField(
                  controller: customRun,
                  decoration: InputDecoration(
                    labelText: 'Number',
                  )),
              Text('${customRun.text}'),
            ])),
            actions: <Widget>[
              FlatButton(
                  child: Text('OK'), onPressed: () => Navigator.pop(context))
            ]);
      });
}

我也已经声明了控制器:

  final TextEditingController customRun = TextEditingController();

(进一步说明)这是我想实时更新的输出,同时在TextField中输入文本,而无需按键盘上的完成

 Text('${customRun.text}'), 

2 个答案:

答案 0 :(得分:2)

对于演示,您可以在这里查看。 Example

1。确定需要重建的文本小部件

首先,我们希望此小部件内的内容将根据用户类型进行重建。

Text('${customRun.text}'),

不幸的是,此小部件以如下方式驻留在widePopUpCustom方法内: 警报对话框的孩子

void widePopUpCustom() async {
    await showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Enter Number'),
          content: SingleChildScrollView(
            child: ListBody(
              children: <Widget>[
                TextField(
                  controller: customRun,
                  decoration: InputDecoration(
                    labelText: 'Number',
                  ),
                ),
                Text('${customRun.text}'), // Needs to be re-rendered
              ],
            ),
          ),
        );
      },
    );
  }

在这种情况下,我们无法在AlertDialog小部件(包括其所有子级ListBodyTextFieldText小部件上)触发重建。

2。拆分AlertDialog并将其转换为有状态小工具

我们考虑将AlertDialog移至AlertWrapper,并将AlertWrapper定义为有状态小工具。因此,稍后,我们可以使用以下代码触发AlertWrapper的重建:

setState((){})

我们可以完成此步骤,将widePopUpCustom()修改为:

Future<void> widePopUpCustom() async {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertWrapper();
      },
    );
  }
  

之前,AlertDialog将被视为无状态,除非我们关闭对话框,否则不会重新渲染该对话框。

3。覆盖有状态小部件以附加侦听器

通过使用有状态的小部件,我们可以探索initState的用法。在这种情况下,我们将addListener附加到customRun TextController。

  

请注意changesOnField方法。每次用户与键盘输入进行交互时都会触发此方法

class AlertWrapper extends StatefulWidget {
  const AlertWrapper({
    Key key,
  }) : super(key: key);

  @override
  _AlertWrapperState createState() => _AlertWrapperState();
}

class _AlertWrapperState extends State<AlertWrapper> {
  final TextEditingController customRun = TextEditingController();

  changesOnField() { 
    print("Updated Text: ${customRun.text}");
    setState(() {}); // Will re-Trigger Build Method
  }

  @override
  void initState() {
    super.initState();
    customRun.addListener(changesOnField);
  }
}

changesOnField将触发重新调用生成方法,然后我们可以更新Text小部件中的值

@override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Enter Number'),
      content: SingleChildScrollView(
        child: ListBody(
          children: <Widget>[
            TextField(
              controller: customRun,
              decoration: InputDecoration(
                labelText: 'Number',
              ),
            ),
            Text('${customRun.text}'), // Finally re-rendered
          ],
        ),
      ),

全功能存储库

您可以查看此仓库。 Github

更多改进

official docs所述,我们需要配置侦听器以优化应用程序。

稍后我们可以添加此代码

@override
void dispose() {
  customRun.dispose();
  super.dispose();
}

答案 1 :(得分:0)

您需要为文本字段添加onChanged(txt)方法,并且必须在其中包含setState()以便每次键入字符时刷新文本

Future<void> widePopUpCustom() async {
  return showDialog<void>(
  context: context,
  builder: (BuildContext context) {
    return AlertDialog(
        title: Text('Enter Number'),
        content: SingleChildScrollView(
            child: ListBody(children: <Widget>[
          TextField(
              controller: customRun,
              decoration: InputDecoration(
                labelText: 'Number',
              )),
              onChanged:(txt){
                 setState((){
                   customRun.text = txt;
                 }),    
                 },
          Text('${customRun.text}'),
        ])),
        actions: <Widget>[
          FlatButton(
              child: Text('OK'), onPressed: () => Navigator.pop(context))
        ]);
  });

}