flutter - Flutter:键盘的选项卡按钮在 TextFormFields 中创建一个空间
问题描述
我已经接受这就是 Flutter 的工作原理,但希望有人能解释为什么物理键盘的选项卡按钮在导航到下一个表单字段时会在 TextFormField 中创建一个空间?我已经使用 RawKeyboardInput 分配选项卡来转移焦点(它确实如此),但它仍然在表单字段中创建了那个单一的空间。
在存储数据时,拥有这个单一空间可能会导致许多问题,所以我更愿意在这一点上修复它,而不是稍后再执行“string.strip()”。
解决方案
我有同样的问题,在 Android 模拟器中测试一个表单,并使用 TAB 移动到下一个TextFormField
.
我的解决方法是TextFormField
将RawKeyboardListener
.
下面的代码有你需要的一切,你可以忽略“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);