如何在颤动中将焦点转移到下一个文本字段?

Posted

技术标签:

【中文标题】如何在颤动中将焦点转移到下一个文本字段?【英文标题】:How to shift focus to the next TextField in Flutter? 【发布时间】:2019-02-08 13:34:21 【问题描述】:

我是 Flutter 的新手。

我正在使用以下小部件构建具有多个文本输入的表单:Form、TextFormField。出现的键盘不显示“下一个”(应该将焦点转移到下一个字段)字段操作,而是“完成”操作(隐藏键盘)。

我在官方文档中寻找任何提示,没有直接找到可以做的事情。 我虽然登陆了 FocusNode(cookbook,api doc)。它提供了通过某个按钮或应用程序上的任何其他操作来转移焦点的机制,但我希望它位于 keyboard 中。

【问题讨论】:

在flutter_gallery中也是一样的。不过应该有一些机制。 目前还不可能(参见github.com/flutter/flutter/issues/11344)。你说的是flutter_gallery的哪一部分? ***.com/questions/51909798/… 哦,我需要找到解决办法。我的意思是 flutter_gallery 也有同样的问题。 【参考方案1】:

截图:


只需使用:

textInputAction: TextInputAction.next:将光标移动到下一个字段。

textInputAction: TextInputAction.done:关闭键盘。

@override
Widget build(BuildContext context) 
  return Scaffold(
    body: Column(
      children: <Widget>[
        TextField(
          decoration: InputDecoration(hintText: 'TextField A'),
          textInputAction: TextInputAction.next, // Moves focus to next.
        ),
        TextField(
          decoration: InputDecoration(hintText: 'TextField B'),
          textInputAction: TextInputAction.next, // Moves focus to next.
        ),
        TextField(
          decoration: InputDecoration(hintText: 'TextField C'),
          textInputAction: TextInputAction.done, // Hides the keyboard.
        ),
      ],
    ),
  );

【讨论】:

感谢,不复杂,容易申请。 @Yuhao 你也可以使用onChanged属性来做到这一点。 @CopsOnRoad 很棒的答案。超级简单。这应该是公认的答案 InputDatePickerFormField 没有实现 textInputAction :/ 如果您使用 TextField 旁边的“密码隐藏/显示按钮”,nextFocus() 将不起作用。【参考方案2】:

找到了实现它的方法。

    显示下一个图标而不是完成 - 将 textInputAction 参数设置为 TextInputAction.next

    使用onFieldSubmitted回调请求下一个字段的焦点节点。

    class FormWidget extends StatelessWidget    
       final focus = FocusNode();
       @override
       Widget build(BuildContext context) 
        return Form(
          child: SingleChildScrollView(
            padding: EdgeInsets.symmetric(horizontal: 16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                TextFormField(
                  textInputAction: TextInputAction.next,
                  autofocus: true,
                  decoration: InputDecoration(labelText: "Input 1"),
                  onFieldSubmitted: (v)
                    FocusScope.of(context).requestFocus(focus);
                  ,
                ),
                TextFormField(
                  focusNode: focus,
                  decoration: InputDecoration(labelText: "Input 2"),
                ),
              ],
            ),
          ),
        );
      
    
    

编辑:如文档 (flutter.io/docs/cookbook/forms/focus) 中所述,我们还需要管理 FocusNode 生命周期。因此,在 init() 方法中初始化 FocusNode 并在父 Widget 的 dispose() 中进行 dispose。 - @AntonDerevyanko

更新:没有FocusNodeFocusScopeNode 也可以实现同样的效果,只需调用FocusScope.of(context).nextFocus(),看看CopsOnRoad solution 是如何做到的。更多信息请查看doc。

【讨论】:

如文档 (flutter.io/docs/cookbook/forms/focus) 中所述, - 我们还需要管理 FocusNode 生命周期。所以,在init()方法中初始化FocusNode,在父Widget的dispose()中dispose。 @Harsh 如果有两个以上的 TextFormField 怎么办? 6个可能 @fajarainul 逻辑保持不变。 onFieldSubmitted 第二场你 requestFocus 第三场 此解决方案存在错误。如果您按 Tab,则 Tab 不仅会更改焦点,还会作为文本添加到表单字段中 - 这不是表单的正常行为。 Tab 应该改变焦点,或者输入文本,而不是两者。 你可以直接调用FocusScope.of(context).requestFocus(focus);而不是focus.requestFocus()【参考方案3】:

这是CopsOnRoad 答案的附加步骤,因为当文本字段之间存在可聚焦小部件时,它在更复杂的 UI 中不起作用,例如:

当密码字段有一个可点击的切换图标时 当字段之间有一个按钮(或其他一些可聚焦的小部件)时...

这里的解决方案是继续调用 'nextFocus()' 直到找到 'EditableText'

   @override
    Widget build(BuildContext context) 
      return Scaffold(
        body: Column(
          children: <Widget>[
            TextField(
              decoration: InputDecoration(hintText: "TextField A"),
              textInputAction: textInputAction1,
              onSubmitted: (_) => context.nextEditableTextFocus(), // move focus to next
            ),
            TextField(
              decoration: InputDecoration(hintText: "TextField B"),
              textInputAction: textInputAction2,
              onSubmitted: (_) => context.nextEditableTextFocus(), // move focus to next
            ),
            MaterialButton(
             onPressed: () ,
             color: Colors.amber,
            ),
            TextField(
              decoration: InputDecoration(hintText: "TextField C"),
              textInputAction: textInputAction3,
              onSubmitted: (_) => FocusScope.of(context).unfocus(), // submit and hide keyboard
            ),
          ],
        ),
      );
    

扩展方法在哪里:

extension Utility on BuildContext 
  void nextEditableTextFocus() 
    do 
      FocusScope.of(this).nextFocus();
     while (FocusScope.of(this).focusedChild?.context?.widget is! EditableText);
  

【讨论】:

这几乎成功了,我不得不用 onEditingComplete: () 替换 onSubmitted: (_) 以使其适用于更困难的布局 非常好,感谢您的帮助。非常适合我。 花了将近 3 个小时研究这个问题。这次真是万分感谢。登录表单从未出现过问题,因为电子邮件字段没有图标和密码,按钮设置为不聚焦,但是当我使用旧密码字段和 2 个新密码字段重置密码时,标准 focusNext 每次都会关闭键盘,直到我找到这个。【参考方案4】:

对于 TextFormFeild 使用可以使用 onFieldSubmitted

TextFormField(
          decoration: InputDecoration(hintText: "Username"),
          textInputAction: TextInputAction.next,
          onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(), // focus to next
        ),
TextFormField(
          decoration: InputDecoration(hintText: "Password"),
          textInputAction: TextInputAction.done,
          onFieldSubmitted: (_) => FocusScope.of(context).unfocus(), // Unfocus and hide keyboard
        ),

不知道确切原因,但 onFieldSubmitted 有时会跳过一个或多个字段,在这种情况下 onEditingComplete 按预期工作

TextFormField(
          decoration: InputDecoration(hintText: "Username"),
          textInputAction: TextInputAction.next,
          onEditingComplete : (_) => FocusScope.of(context).nextFocus(), // focus to next
        ),
TextFormField(
          decoration: InputDecoration(hintText: "Password"),
          textInputAction: TextInputAction.done,
          onEditingComplete : (_) => FocusScope.of(context).unfocus(), // Unfocus and hide keyboard
        ),

【讨论】:

@rexxar 抱歉,这是一个打字错误,我现在已经编辑过了。【参考方案5】:

对我来说,这行得通,它会在输入第一个数字时移动到下一个输入

Row(
                  children: <Widget>[
                    Expanded(
                      child: TextFormField(
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).nextFocus(),
                          controller:c1 ,)
                    ),
                    SizedBox(
                      width: 20.0,
                    ),
                    Expanded(
                      child: TextFormField(
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).nextFocus(),
                          controller:c2 ,),
                    ),
                    SizedBox(
                      width: 20.0,
                    ),
                    Expanded(
                      child: TextFormField(
                          controller:c3 ,
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).nextFocus(),),
                    ),
                    SizedBox(
                      width: 20.0,
                    ),
                    Expanded(
                      child: TextFormField(
                          controller:c4 ,
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).nextFocus(),),
                    ),
                    SizedBox(
                      width: 20.0,
                    ),
                    Expanded(
                      child: TextFormField(

                          controller:c5 ,
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).nextFocus(),),
                    ),
                    SizedBox(
                      width: 20.0,
                    ),
                    Expanded(
                      child: TextFormField(
                          controller:c6 ,
                        textInputAction: TextInputAction.next,
                        onChanged: (_) => FocusScope.of(context).unfocus(),
                          ),
                    )
                  ],
                )

【讨论】:

我使用您的代码将文本字段设置为彼此相邻,我是颤振的初学者。谢谢!【参考方案6】:

我尝试只添加textInputActionproperty,它没有其他任何东西就可以工作。

也许新版本的 Flutter 改进了这个功能? 还是需要 StatefulWidget 和 FormKey 才能正常工作?

我正在使用 Flutter(稳定频道,1.22.5)

试一试:

class MyForm extends StatefulWidget 
  @override
  State createState() => _myFormState();


class _myFormState extends State<MyForm> 
  //
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) 
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            // *** Just added this
            textInputAction: TextInputAction.next,
            decoration: const InputDecoration(
              labelText: 'Input 1',
            ),
          ),
          TextFormField(
            textInputAction: TextInputAction.done,
            decoration: const InputDecoration(
              labelText: 'Input 2',
            ),
          )
        ],
      ),
    );
  

【讨论】:

你是对的,我认为它已经修复了here。实际上现在调用nextFocus 是错误的,如果你将textInputAction 设置为TextInputAction.next,会导致跳过一个字段。所以这是一个突破性的变化。 我不知道,返回键不同的动作设置已经存在很多年了(ios中超过10个)。但是关于 Flutter 支持,我不能告诉你同样的事情,也许这种行为最近已经被“映射”了。无论如何,只要不需要特定的行为,我会坚持使用 textInputAction,因为它是应用程序的默认设置,并且用户已经习惯了 :)【参考方案7】:

我用过 onSubmitted 而不是 onFieldSubmitted

示例代码

      TextField(
                textInputAction: TextInputAction.next,
                onSubmitted: (_) => FocusScope.of(context).nextFocus(),
                controller: _phoneController,
                decoration: const InputDecoration(
                  labelText: 'Phone number',
                ),
                style: TextStyle(fontSize: 16.0, color: Colors.white),
              ),

【讨论】:

【参考方案8】:

您可以使用这个辅助函数来关注下一个文本字段:

void focusNextTextField(BuildContext context) 
  do 
    var foundFocusNode = FocusScope.of(context).nextFocus();
    if (!foundFocusNode) return;
   while (FocusScope.of(context).focusedChild.context.widget is! EditableText);

【讨论】:

以上是关于如何在颤动中将焦点转移到下一个文本字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中取消焦点文本字段

如何在颤动中取消焦点文本字段?

按下 Enter 时将焦点转移到下一个字段

将焦点从一个组件转移到另一个组件

按下回车键时如何将焦点移到下一个字段?

如何在颤动中将列表数据从一个屏幕传递到另一个屏幕(StatefulWidget)