【问题标题】:Multiple cursor in form's TextFormField flutter表单的 TextFormField 中的多个光标颤动
【发布时间】:2019-09-26 00:37:39
【问题描述】:

我是 Flutter 的新手,我尝试使用以下代码实现此屏幕,在此我面临一个奇怪的多光标 UI。

重现此问题的步骤:

  • 随意点击任意TextFormField,软键盘弹出,按下底部返回导航键,键盘隐藏..
  • 当我多次执行此过程时,每个TextFormField 中的光标标记都会闪烁。

错在哪里?

import 'package:flutter/material.dart';
import 'package:thought_factory/utils/colors.dart';

class RegisterScreen extends StatefulWidget {
@override
_RegisterScreenState createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
static var _keyValidationForm = GlobalKey<FormState>();
TextEditingController _textEditConName = TextEditingController();
TextEditingController _textEditConEmail = TextEditingController();
TextEditingController _textEditConPassword = TextEditingController();
TextEditingController _textEditConConfirmPassword = TextEditingController();
bool isPasswordVisible = false;
bool isConfirmPasswordVisible = false;

@override
void initState() {
isPasswordVisible = false;
isConfirmPasswordVisible = false;
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
  backgroundColor: AppColors.colorGrey,
  body: SingleChildScrollView(
    child: Padding(
        padding: EdgeInsets.only(top: 32.0),
        child: Column(
          children: <Widget>[
            getWidgetImageLogo(),
            getWidgetRegistrationCard(),
          ],
        )),
  ),
);
}

Widget getWidgetImageLogo() {
return Container(
    alignment: Alignment.center,
    child: Padding(
      padding: const EdgeInsets.only(top: 32, bottom: 32),
      child: Icon(Icons.ac_unit),
    ));
 }

Widget getWidgetRegistrationCard() {
final FocusNode _passwordEmail = FocusNode();
final FocusNode _passwordFocus = FocusNode();
final FocusNode _passwordConfirmFocus = FocusNode();

return Padding(
  padding: const EdgeInsets.only(left: 16.0, right: 16.0),
  child: Card(
    color: Colors.white,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(12.0),
    ),
    elevation: 10.0,
    child: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Form(
        key: _keyValidationForm,
        child: Column(
          children: <Widget>[
            Container(
              alignment: Alignment.center,
              width: double.infinity,
              child: Text(
                'Register',
                style: TextStyle(
                    fontSize: 18.0, color: AppColors.colorBlack),
              ),
            ), // title: login
            Container(
              child: TextFormField(
                controller: _textEditConName,
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.next,
                validator: _validateUserName,
                onFieldSubmitted: (String value) {
                  FocusScope.of(context).requestFocus(_passwordEmail);
                },
                decoration: InputDecoration(
                    labelText: 'Full name',
                    //prefixIcon: Icon(Icons.email),
                    icon: Icon(Icons.perm_identity)),
              ),
            ), //text field : user name
            Container(
              child: TextFormField(
                controller: _textEditConEmail,
                focusNode: _passwordEmail,
                keyboardType: TextInputType.emailAddress,
                textInputAction: TextInputAction.next,
                validator: _validateEmail,
                onFieldSubmitted: (String value) {
                  FocusScope.of(context).requestFocus(_passwordFocus);
                },
                decoration: InputDecoration(
                    labelText: 'Email',
                    //prefixIcon: Icon(Icons.email),
                    icon: Icon(Icons.email)),
              ),
            ), //text field: email
            Container(
              child: TextFormField(
                controller: _textEditConPassword,
                focusNode: _passwordFocus,
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.next,
                validator: _validatePassword,
                onFieldSubmitted: (String value) {
                  FocusScope.of(context)
                      .requestFocus(_passwordConfirmFocus);
                },
                obscureText: !isPasswordVisible,
                decoration: InputDecoration(
                    labelText: 'Password',
                    suffixIcon: IconButton(
                      icon: Icon(isPasswordVisible
                          ? Icons.visibility
                          : Icons.visibility_off),
                      onPressed: () {
                        setState(() {
                          isPasswordVisible = !isPasswordVisible;
                        });
                      },
                    ),
                    icon: Icon(Icons.vpn_key)),
              ),
            ), //text field: password
            Container(
              child: TextFormField(
                  controller: _textEditConConfirmPassword,
                  focusNode: _passwordConfirmFocus,
                  keyboardType: TextInputType.text,
                  textInputAction: TextInputAction.done,
                  validator: _validateConfirmPassword,
                  obscureText: !isConfirmPasswordVisible,
                  decoration: InputDecoration(
                      labelText: 'Confirm Password',
                      suffixIcon: IconButton(
                        icon: Icon(isConfirmPasswordVisible
                            ? Icons.visibility
                            : Icons.visibility_off),
                        onPressed: () {
                          setState(() {
                            isConfirmPasswordVisible =
                                !isConfirmPasswordVisible;
                          });
                        },
                      ),
                      icon: Icon(Icons.vpn_key))),
            ),
            Container(
              margin: EdgeInsets.only(top: 32.0),
              width: double.infinity,
              child: RaisedButton(
                color: AppColors.colorAccent,
                textColor: Colors.white,
                elevation: 5.0,
                padding: EdgeInsets.only(top: 16.0, bottom: 16.0),
                child: Text(
                  'Register',
                  style: TextStyle(fontSize: 16.0),
                ),
                onPressed: () {
                  if (_keyValidationForm.currentState.validate()) {
                    _onTappedButtonRegister();
                  }
                },
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(25.0)),
              ),
            ), //button: login
            Container(
                margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text(
                      'Already Register? ',
                    ),
                    InkWell(
                      splashColor: AppColors.colorAccent.withOpacity(0.5),
                      onTap: () {
                        _onTappedTextlogin();
                      },
                      child: Text(
                        ' Login',
                        style: TextStyle(
                            color: AppColors.colorAccent,
                            fontWeight: FontWeight.bold),
                      ),
                    )
                  ],
                ))
          ],
        ),
      ),
    ),
  ),
);
}

String _validateUserName(String value) {
return value.trim().isEmpty ? "Name can't be empty" : null;
}

String _validateEmail(String value) {
Pattern pattern =
    r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value)) {
  return 'Invalid Email';
} else {
  return null;
}
}

String _validatePassword(String value) {
return value.length < 5 ? 'Min 5 char required' : null;
}

String _validateConfirmPassword(String value) {
return value.length < 5 ? 'Min 5 char required' : null;
}

void _onTappedButtonRegister() {}

void _onTappedTextlogin() {}
}
 

【问题讨论】:

  • 如果通过添加autofocus: false 禁用每个TextFormField 的自动对焦会怎样?
  • 您的代码对我来说很好用。尝试升级你的颤振
  • 感谢 @AlexLushiku 和 @nuts 的宝贵时间,没有一个对我有用,除了这个解决方案。我刚刚将所有 focusNode 局部变量设置为解决问题的全局范围。
  • @SaravanaRaja 太棒了!不过请考虑回答您自己的问题,以便其他人知道如何解决它并且您的问题得到解决。
  • 我有同样的问题,但我认为我的问题是因为我使用 StatelessWidget,它会导致问题 imo。我该怎么办?编辑:我转换成 StatefulWidget,即使你使用 BLoC,也不要对 TextField 使用 Stateless。

标签: flutter forms dart text-cursor


【解决方案1】:

正如 OP 在评论部分回答的那样。

将所有focusNode从局部变量声明到解决问题的全局范围内。

我在Widget build(BuildContext context) 方法中使用focusNode。显示多个光标。

class _LoginScreenState extends State<LoginScreen> {

@override
  Widget build(BuildContext context) {

    final FocusNode _emailFocus = FocusNode();
    final FocusNode _passwordFocus = FocusNode();

解决方案 - 在全球范围内声明 focusNode

class _LoginScreenState extends State<LoginScreen> {

  final FocusNode _emailFocus = FocusNode();
  final FocusNode _passwordFocus = FocusNode();

  @override
  Widget build(BuildContext context) {

【讨论】:

  • 如果你有 20000 个文本字段呢?
【解决方案2】:

从此方法中移除了本地范围focusNode

Widget getWidgetRegistrationCard() {
final FocusNode _passwordEmail = FocusNode(); //removed 
final FocusNode _passwordFocus = FocusNode(); //removed
final FocusNode _passwordConfirmFocus = FocusNode(); //removed
.
. 
.
} 

并声明它全局解决了我的问题。

class RegisterScreen extends StatefulWidget {
@override
_RegisterScreenState createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {
static var _keyValidationForm = GlobalKey<FormState>();

final FocusNode _passwordEmail = FocusNode();  //added globally
final FocusNode _passwordFocus = FocusNode();  //added globally
final FocusNode _passwordConfirmFocus = FocusNode();  //added globally
.
. 
.

【讨论】:

  • 如果你有 20000 个文本字段呢?
猜你喜欢
  • 2021-11-02
  • 2020-10-11
  • 2020-12-02
  • 2020-01-25
  • 2019-10-17
  • 2022-11-01
  • 2021-12-27
  • 2021-06-11
  • 2022-01-12
相关资源
最近更新 更多