标签 Flutter 下的文章

问题及原因分析

RFID 阅读器在读取到数据后,会寻找具有焦点的 TextField,并将读取到的数据输出到 TextField 中,之后发送一个回车按键事件。

这里有个问题,不能使用 TextFieldonChanged 方法来获取输入框中的文字,因为此事件会触发多次。也不能使用 onEditingComplete 或者 onSubmitted,因为在 TextField 接收完数据之前,就会触发回车键,导致 TextField 失去焦点,并中断接收数据。至于为什么回车事件会先于数据接收完成之前执行,我猜测,大概是因为不断重建 TextField Widget 的过程消耗了太多时间,同时 Flutter 内部存在一个类似 JavaScript 的事件循环机制,导致回车事件排在了接下来的 onChanged 事件之前。说实话,我是一个初学者,对 Flutter 了解不够深入。

解决思路

想要解决这个问题,貌似不能从 TextField 本身的属性配置上来下手了。既然 TextField 接收数据有延迟,那就需要定时检测 TextField 关联的 TextEditingController.text 在一定时间内是否还会变化,根据变化情况来确定数据是还在接收,还是已经接收完毕。

焦点的问题

在实现上面的定时检测代码之前,还有一个问题需要解决:焦点问题。上文提到了,TextField 在没接收完数据之前就会触发回车事件并失去焦点。一旦 TextField 失去焦点,便不再接收剩余的数据。经过不断尝试,将 TextFieldkeyboardType 设置为 TextInputType.multiline 能够解决这个问题。

以下是官方文档对 TextInputType.multiline 的解释:

Optimize for multiline textual information.

Requests the default platform keyboard, but accepts newlines when the enter key is pressed. This is the input type used for all multiline text fields.

优化多行文本信息。

请求默认平台键盘,但按下enter键时接受换行。这是用于所有多行文本字段的输入类型

但设置了这一属性,TextField 里面的文字在遇到回车时不就换行了吗?没有关系,只要我们不设置它的 maxLines 属性为 1 以外的值,这就不会发生。maxLines 的默认值就是 1,所以,总而言之,我们只要设置 keyboardType 属性为 TextInputType.multiline 就行了,无需考虑其他问题。

定时检测的实现

下面需要一个定时器来检测 TextField 的内容。核心思想是,定时检查 TextFieldTextEditingController.text 的值是否与上一次保存的旧值相同,如果不同,说明接收仍在继续,这时要将新值保存起来,用于下一次对比。如果新值和旧值相同,就认为数据接收已经完毕。

代码如下:

final inputController = TextEditingController();

Timer? timer;
int count = 0; //设置了一个定时器执行次数
String oldVal = '';

timer = Timer.periodic(const Duration(milliseconds: 500), (_timer) async {
  if (
       count >= 10 || 
       (
          inputController.text != '' && 
          oldVal != '' && 
          inputController.text == oldVal
       )
  ) {
    _timer.cancel();
    //do something you want...
  } else {
    count++;
    oldVal = inputController.text;
  }
});

END