【问题标题】:Automatically Switch Textfields自动切换文本字段
【发布时间】:2022-01-13 15:31:10
【问题描述】:

我想实现一个年龄验证过程,但要获得年龄,我需要可以输入生日的文本字段。这是我现在拥有的文本字段的代码:

class TextFieldAgeInput extends StatelessWidget {
  TextFieldAgeInput({
    Key? key,
    required this.textController,
    required this.leftPadding,
    required this.hintText,
  }) : super(key: key);

  TextEditingController textController;
  double leftPadding;
  String hintText;
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(
        left: leftPadding,
        top: 5,
      ),
      child: Container(
        height: 40,
        width: 30,
        decoration: BoxDecoration(
          color: backgroundColor,
          borderRadius: BorderRadius.circular(10),
          border: Border.all(
            width: 1,
            color: primaryColor,
          ),
        ),
        child: Padding(
          padding: const EdgeInsets.only(
            bottom: 5,
            left: 1,
          ),
          child: TextField(
            keyboardType: TextInputType.number,
            style: GoogleFonts.poppins(
              textStyle: const TextStyle(
                color: mainTextColor,
                fontSize: 15,
                fontWeight: FontWeight.w600,
              ),
            ),
            textAlign: TextAlign.center,
            controller: textController,
            decoration: InputDecoration(
              hintText: hintText,
              hintStyle: GoogleFonts.poppins(
                textStyle: const TextStyle(
                  color: primaryColor,
                  fontSize: 11,
                  fontWeight: FontWeight.w600,
                ),
              ),
              border: InputBorder.none,
            ),
            onChanged: (textController) {
              TextInputAction.next;
            },
          ),
        ),
      ),
    );
  }
}

在屏幕的代码中,我使用填充等多次调用小部件。现在,当给出 1 个数字(键盘 = numberkeyboard)时,如何使文本字段切换到下一个?

提前感谢您的帮助

【问题讨论】:

    标签: flutter dart widget textfield stateful


    【解决方案1】:

    您的代码中存在基本错误,如下所示:

    onChanged: (textController) {
      TextInputAction.next;
    },
    

    onChanged 属性是为您提供更新文本的回调。它不会给你textController。在正文中,您使用的定义不执行任何操作 TextInputAction.next;。这应该以keyboardType: TextInputType.number 的形式分配给TextField

    无论如何,下面是我不久前写的一个完全可运行的示例,它解决了您要解决的相同问题。该示例用于验证码,但也适用于出生年份(因为两者都是四个字段)。

    在示例中,您会看到每个字段都有自己的TextEditingControllerFocusNode。我们使用控制器设置/检索值,而焦点节点用于将焦点从一个字段移动到另一个字段。

    该示例还使用一种解决方法来检测用户何时单击退格键(请参阅底部的注释),因此您会看到 zero width space 字符添加到控制器中,但在我们添加到 code 时被删除(将是年龄在你的例子)。代码中有这方面的注释。

    您可以在此处查看 DartPad 上运行的代码:Multiple Text Fields Example 或者您可以将其复制到编辑器并运行它:

    // ignore_for_file: avoid_function_literals_in_foreach_calls, avoid_print
    import 'package:flutter/material.dart';
    
    void main() => runApp(MultipleTextFieldsExampleApp());
    
    
    
    class MultipleTextFieldsExampleApp extends StatelessWidget {
      const MultipleTextFieldsExampleApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      final String title;
      const MyHomePage({Key? key, required this.title}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(title),
          ),
          body: const 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 = ['', '', '', ''];
    
      late List<TextEditingController> controllers;
      late List<FocusNode> focusNodes;
    
      @override
      void 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();
        });
      }
    
      void printValues() {
        print(code);
      }
    
      @override
      void 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: const 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');
                  },
                ),
              );
            },
          ),
        );
      }
    }
    
    

    在 Flutter 中,当 TextField 为空时,退格键不会触发 onChanged,因此存在解决方法。

    【讨论】:

    • 非常感谢,这对我帮助很大。当我上传代码时,我忘了删除我测试过的东西(textinputaction 等),这就是为什么它没有做任何事情我只是想尝试一下,但因为我从未使用过焦点节点并且不知道如何使用退格我卡住了。仍然非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 2016-02-14
    相关资源
    最近更新 更多