首页 > 解决方案 > Flutter:键盘的选项卡按钮在 TextFormFields 中创建一个空间

问题描述

我已经接受这就是 Flutter 的工作原理,但希望有人能解释为什么物理键盘的选项卡按钮在导航到下一个表单字段时会在 TextFormField 中创建一个空间?我已经使用 RawKeyboardInput 分配选项卡来转移焦点(它确实如此),但它仍然在表单字段中创建了那个单一的空间。

在此处输入图像描述

在存储数据时,拥有这个单一空间可能会导致许多问题,所以我更愿意在这一点上修复它,而不是稍后再执行“string.strip()”。

标签: flutterdart

解决方案


我有同样的问题,在 Android 模拟器中测试一个表单,并使用 TAB 移动到下一个TextFormField.

我的解决方法是TextFormFieldRawKeyboardListener.

下面的代码有你需要的一切,你可以忽略“LoginStore”相关的东西,因为我使用的是 MobX 状态管理器。

样本:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {

  TextEditingController _userNameController = TextEditingController();
  TextEditingController _passwordController = TextEditingController();

  FocusNode _usernameFocusNode;
  FocusNode _passwordKeyboardFocusNode;
  FocusNode _passwordFocusNode;

  LoginStore _loginStore;

  @override
  void initState() {
    super.initState();
    _passwordFocusNode = FocusNode();
    _passwordKeyboardFocusNode = FocusNode();
    _usernameFocusNode = FocusNode();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: true,
      body: Material(
        child: Stack(
          children: <Widget>[
            userNameTextField(),
            SizedBox(height: 12.0),
            passwordTextField(),
            SizedBox(height: 12.0),
            loginButton()
          ]
        )
      )
    );
  }

  Widget userNameTextField() {
    //mobx observer omitted
    return RawKeyboardListener(
      focusNode: _usernameFocusNode,
      child: TextFormField(
        decoration: InputDecoration(
          hintText: "username",
          errorText: _loginStore.formErrorStore.username
        ),
        controller: _userNameController,
        onChanged: (dynamic value) {
          _loginStore.setUserName(_userNameController.text);
        },
        onFieldSubmitted: (dynamic value) {
          FocusScope.of(context).requestFocus(_passwordFocusNode);
        }
      ),
      onKey: (RawKeyEvent event) {
        if (event.isKeyPressed(LogicalKeyboardKey.tab)) {
          var currentText = _userNameController.text;
          var textWithoutTab = currentText.replaceAll("\t", "");
          //update the controller and the store
          _userNameController.text = textWithoutTab;
          _loginStore.setUserName(_userNameController.text);
          //move the focus to the password form
          FocusScope.of(context).requestFocus(_passwordFocusNode);
        }
      }
    );
  }

  Widget passwordTextField() {
    //mobx observer omitted
    return RawKeyboardListener(
      focusNode: _passwordKeyboardFocusNode,
      child: TextFormField(
        decoration: InputDecoration(
          hintText: "password",
          errorText: _loginStore.formErrorStore.password
        ),
        controller: _passwordController,
        onChanged: (dynamic value) {
          _loginStore.setPassword(_passwordController.text);
        }
      ),
      onKey: (RawKeyEvent event) {
        if (event.isKeyPressed(LogicalKeyboardKey.tab)) {
          var currentText = _passwordController.text;
          var textWithoutTab = currentText.replaceAll("\t", "");
          //update the controller and the store
          _passwordController.text = textWithoutTab;
          _loginStore.setPassword(_passwordController.text);
        }
      }
    );
  }

  Widget loginButton() {
    //add a login button as you want ....
  }

  @override
  void dispose() {
    // Clean up the controller when the Widget is removed from the Widget tree
    _userNameController.dispose();
    _passwordController.dispose();
    _passwordKeyboardFocusNode.dispose();
    _passwordFocusNode.dispose();
    _usernameFocusNode.dispose();
    super.dispose();
  }

}

请记住,使用这样的自定义值设置文本控制器_passwordController.text = textWithoutTab;不会触发onChanged回调,如果您执行表单验证,您需要使状态与更新的文本保持同步,这就是为什么我必须执行另一个调用_loginStore.setPassword(_passwordController.text);


推荐阅读