自动切换文本域

Automatically Switch Textfields

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

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 个数字(键盘 = 数字键盘)时,如何使文本字段切换到下一个?

提前感谢您的帮助

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

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

onChanged 属性 是一个回调,为您提供更新后的文本。它不会给你 textController。在正文中,您使用的是什么都不做的定义 TextInputAction.next;。这应该分配给 TextField 作为 keyboardType: TextInputType.number.

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

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

该示例还使用变通方法来检测用户何时单击退格键(请参阅底部的注释)因此您会看到 zero width space 字符已添加到控制器,但在我们添加到 [=20= 时被删除] (在你的例子中是年龄)。代码中有关于此的注释。

您可以在此处查看 DartPad 上的代码 运行ning: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,因此存在解决方法。