您的代码中存在基本错误,如下所示:
onChanged: (textController) {
TextInputAction.next;
},
onChanged 属性是为您提供更新文本的回调。它不会给你textController。在正文中,您使用的定义不执行任何操作 TextInputAction.next;。这应该以keyboardType: TextInputType.number 的形式分配给TextField。
无论如何,下面是我不久前写的一个完全可运行的示例,它解决了您要解决的相同问题。该示例用于验证码,但也适用于出生年份(因为两者都是四个字段)。
在示例中,您会看到每个字段都有自己的TextEditingController 和FocusNode。我们使用控制器设置/检索值,而焦点节点用于将焦点从一个字段移动到另一个字段。
该示例还使用一种解决方法来检测用户何时单击退格键(请参阅底部的注释),因此您会看到 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,因此存在解决方法。