(Flutter) 如何在不按“完成”的情况下自动监听 TextField 中的变化?

Posted

技术标签:

【中文标题】(Flutter) 如何在不按“完成”的情况下自动监听 TextField 中的变化?【英文标题】:(Flutter) How to listen changes in TextField automatically, without pressing "Done"? 【发布时间】:2019-03-18 15:38:21 【问题描述】:

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

这是我的源代码:

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'), 

【问题讨论】:

【参考方案1】:

对于演示,您可以看这里。 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 并将其转换为 Stateful Widget

我们考虑将AlertDialog 移动到AlertWrapper,并将AlertWrapper 定义为Stateful Widget。所以稍后,我们可以使用以下代码触发AlertWrapper 的重建:

setState(())

我们可以结束这一步,将widePopUpCustom()修改为这个:

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

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

3。覆盖 Stateful Widget 以附加监听器

通过使用有状态小部件,我们可以探索 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 会触发 build 方法被重新调用,然后我们可以更新 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
          ],
        ),
      ),

完整的工作回购

你可以看看这个 repo。 Github

更多改进

正如official docs 所说,我们需要配置监听器来优化应用程序。

稍后我们可以添加此代码

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

【讨论】:

【参考方案2】:

您需要为文本字段添加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))
        ]);
  );

【讨论】:

它仍然没有实时更新输出。只有当我在键盘上按“完成”时它才会更新。澄清一下:Text('$customRun.text'), is my output 我建议您添加一个新的字符串变量来存储输入并在文本视图中输出它,我经常使用它,它可以为我添加的每个字母工作和更新

以上是关于(Flutter) 如何在不按“完成”的情况下自动监听 TextField 中的变化?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不按下按钮的情况下自动处理 <textarea> 的内容?

如何在不单击后退按钮的情况下隐藏键盘[重复]

我可以在不按任何选择按钮的情况下从我的 uipickerview 行的值自动更新我的 uitextview 吗?

如何在不按 Enter C# WPF 的情况下检测我的键盘

iOS - 如何在不按下按钮的情况下在视图控制器之间移动

Pygame:如何在不按任何键的情况下使精灵面对相同的方向