【问题标题】:how to focus the next text field automatically after user input 1 character in flutter用户在颤动中输入1个字符后如何自动聚焦下一个文本字段
【发布时间】:2021-09-08 21:31:40
【问题描述】:

我有 4 个textFormField 小部件。一旦用户完成了第一个文本字段,我想自动关注下一个textField。有没有办法在 Flutter 中做到这一点?任何人请分享,在此先感谢:)

【问题讨论】:

    标签: flutter dart flutter-layout flutter-dependencies flutter-web


    【解决方案1】:

    这可以在 Flutter 中以不同的方式完成,我将尝试分享其中最简单的一种。在进入答案之前,值得一提的是以下问题:

    在 Flutter 中,当 TextField 为空时,退格不会发送任何事件(即不会调用 TextField.onChanged)。在您的情况下,如果用户在第三个字段并且他们按退格键返回到第二个字段,那么如果没有链接问题中讨论的一些解决方法,就无法捕获该按键。简而言之,您需要添加一个零宽度空格字符(它不会被渲染但存在于字符串中)来检测退格事件。

    我之所以提到这个问题,是因为我分享了一个利用零宽度空格字符(简称 zwsp)的示例。

    在以下示例中,我只是创建了两个列表,其中包含:

    • FocusNode 每个字段
    • TextEditingController 对应每个字段。

    基于索引,您可以通过调用将焦点带到特定字段: FocusNode.requestFocus().

    同样,您可以通过调用FocusNode.unfocus 来移除焦点,或者您可以通过调用FocusScope.of(context).unfocus(); 从任何位置移除任何焦点(在下面的示例中,它用于在插入最后一个字符后隐藏键盘)。

    话虽如此,这是一个完整的示例,您可以复制并粘贴来试用:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      final String title;
      MyHomePage({Key key, this.title}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: Center(child: CodeField()),
        );
      }
    }
    
    /// zero-width space character
    ///
    /// this character can be added to a string to detect backspace.
    /// The value, from its name, has a zero-width so it's not rendered
    /// in the screen but it'll be present in the String.
    ///
    /// The main reason this value is used because in Flutter mobile,
    /// backspace is not detected when there's nothing to delete.
    const zwsp = '\u200b';
    
    // the selection is at offset 1 so any character is inserted after it.
    const zwspEditingValue = TextEditingValue(text: zwsp, selection: TextSelection(baseOffset: 1, extentOffset: 1));
    
    class CodeField extends StatefulWidget {
      const CodeField({Key key}) : super(key: key);
    
      @override
      _CodeFieldState createState() => _CodeFieldState();
    }
    
    class _CodeFieldState extends State<CodeField> {
      List<String> code = ['', '', '', ''];
    
      List<TextEditingController> controllers;
      List<FocusNode> focusNodes;
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        focusNodes = List.generate(4, (index) => FocusNode());
        controllers = List.generate(4, (index) {
          final ctrl = TextEditingController();
          ctrl.value = zwspEditingValue;
          return ctrl;
        });
    
        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
          // give the focus to the first node.
          focusNodes[0].requestFocus();
        });
      }
    
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        focusNodes.forEach((focusNode) {
          focusNode.dispose();
        });
        controllers.forEach((controller) {
          controller.dispose();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: List.generate(
            4,
            (index) {
              return Container(
                width: 20,
                height: 20,
                margin: const EdgeInsets.all(10),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                ),
                child: TextField(
                  controller: controllers[index],
                  focusNode: focusNodes[index],
                  maxLength: 2,
                  keyboardType: TextInputType.number,
                  decoration: InputDecoration(
                    counterText: "",
                  ),
                  onChanged: (value) {
                    if (value.length > 1) {
                      // this is a new character event
                      if (index + 1 == focusNodes.length) {
                        // do something after the last character was inserted
                        FocusScope.of(context).unfocus();
                      } else {
                        // move to the next field
                        focusNodes[index + 1].requestFocus();
                      }
                    } else {
                      // this is backspace event
    
                      // reset the controller
                      controllers[index].value = zwspEditingValue;
                      if (index == 0) {
                        // do something if backspace was pressed at the first field
    
                      } else {
                        // go back to previous field
                        controllers[index - 1].value = zwspEditingValue;
                        focusNodes[index - 1].requestFocus();
                      }
                    }
                    // make sure to remove the zwsp character
                    code[index] = value.replaceAll(zwsp, '');
                    print('current code = $code');
                  },
                ),
              );
            },
          ),
        );
      }
    }
    
    
    

    【讨论】:

    • 不幸的是,这打破了 iOS 上的大写
    【解决方案2】:

    您可能希望在每个TextFormField 上使用FocusNode,这样,一旦您的用户在TextFormField 中输入文本,您就可以在TextFormField 调用的回调onChanged 中使用myNextTextFieldFocusNode.requestFocus()

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

    【讨论】:

      【解决方案3】:

      您可以使用 onChanged 和 nodefocus 属性。当 onchanged 被调用时,请参考下一个文本字段。

      初始化一个焦点节点;

        late FocusNode myFocusNode;
      
        @override
        void initState() {
          super.initState();
      
          myFocusNode = FocusNode();
        }
      
        @override
        void dispose() {
          // Clean up the focus node when the Form is disposed.
          myFocusNode.dispose();
      
          super.dispose();
        }
      

      onChanged 属性;

      TextField(
        focusNode: myFocusNode1,
        onChanged: (text) {
      
         myFocusNode2.requestFocus();// I could not remember the correct usage please check
        },
      ),
      

      【讨论】:

        猜你喜欢
        • 2019-12-02
        • 1970-01-01
        • 1970-01-01
        • 2019-02-08
        • 1970-01-01
        • 2021-07-11
        • 2021-11-02
        • 1970-01-01
        • 2020-07-03
        相关资源
        最近更新 更多