自动切换文本域
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
.
无论如何,下面是我不久前写的一个完全 运行 可行的示例,它解决了您正在尝试解决的相同问题。该示例是验证码,但它也适用于出生年份(因为都是四个字段)。
在示例中,您会看到每个字段都有自己的 TextEditingController
和 FocusNode
。我们将控制器用于 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
,因此存在解决方法。
我想实施一个年龄验证过程,但要获得年龄,我需要可以输入生日的文本字段。这是我现在拥有的文本字段的代码:
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
.
无论如何,下面是我不久前写的一个完全 运行 可行的示例,它解决了您正在尝试解决的相同问题。该示例是验证码,但它也适用于出生年份(因为都是四个字段)。
在示例中,您会看到每个字段都有自己的 TextEditingController
和 FocusNode
。我们将控制器用于 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
,因此存在解决方法。