Flutter中如何将焦点转移到下一个TextField?

问题描述 投票:0回答:12

我是 Flutter 新手。

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

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

flutter dart flutter-layout focus textfield
12个回答
312
投票

截图:


只需使用:

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.
        ),
      ],
    ),
  );
}

134
投票

找到了实现它的方法。

  1. 显示下一个图标而不是完成 - 将

    textInputAction
    参数设置为
    TextInputAction.next

  2. 使用

    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() 中进行处理。 - @AntonDerevyanko

更新:无需

FocusNode
FocusScopeNode
,只需调用
FocusScope.of(context).nextFocus()
即可实现相同目的,请查看 CopsOnRoad 解决方案 了解如何做到这一点。欲了解更多信息,请查看doc


35
投票

这些是 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);
  }
}

14
投票

对于TextFormFeild使用可以使用onFieldSubscribed

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
        ),

不知道确切的原因,但 onFieldSubscribed 有时会跳过一个或多个字段,在这种情况下 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
        ),

12
投票

对我来说,这有效,输入第一个数字后它会移动到下一个输入

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(),
                          ),
                    )
                  ],
                )

9
投票

感谢@haytham-anmar 分享的扩展和@natesh-bhat 的扩展!

但是,由于

EditableText
树上的重大更改,这对于 Flutter 的未来版本将不再起作用(参考:将文本编辑操作移至 EditableTextState #90684)。

请考虑以下迁移代码:

迁移前:

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

迁移后:

extension Utility on BuildContext {
  void nextEditableTextFocus() {
    do {
      FocusScope.of(this).nextFocus();
    } while (FocusScope.of(this).focusedChild!.context == null);
  }
}

6
投票

我尝试仅添加

textInputAction
属性,并且无需其他任何内容即可工作。

也许新版本的 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',
            ),
          )
        ],
      ),
    );
  }
}

4
投票

在最新的 flutter 版本中,如果表单字段中有可见图标或任何其他图标,则上述所有方法都不起作用。你可以用这个方法让它发挥作用

为每个 TextFormFields 创建 FocusNode,将其分配给 TextFormFields 并在编辑完成时,将焦点请求到下一个 Node。

  FocusNode textFieldOne = FocusNode();
  FocusNode textFieldTwo = FocusNode();

  // ...

  TextFormField(
        onChanged: (_) {
           textFieldTwo.requestFocus();
        },
        focusNode: textFieldOne,
        controller: textController,
  )```

2
投票

我用过

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),
              ),

2
投票

您可以使用此辅助函数来聚焦下一个文本字段:

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

1
投票

希望有用

textInputAction: TextInputAction.next,
        onEditingComplete: () {
             FocusScope.of(context).nextFocus();
        },
        onSubmitted: (value) {
            if (value.isNotEmpty) {
                  FocusScope.of(context).nextFocus();
            }
        },

0
投票

按照您想要的方法使用

FocusScope.of(context).nextFocus();

© www.soinside.com 2019 - 2024. All rights reserved.