Flutter:如何为多个TextFormField实现共用屏幕数字键盘
解决方案
要实现同一页面多个输入框共用自定义数字键盘,核心是跟踪当前激活的输入框控制器,让键盘操作指向这个激活的控制器。以下是具体实现步骤和修改后的代码:
1. 页面层实现:管理多个输入框与焦点
在你的页面State类中,为每个TextFormField创建独立的TextEditingController和FocusNode,并监听焦点变化来记录当前激活的控制器:
class MultiInputPage extends StatefulWidget { const MultiInputPage({super.key}); @override State<MultiInputPage> createState() => _MultiInputPageState(); } class _MultiInputPageState extends State<MultiInputPage> { // 为每个输入框创建独立控制器和焦点节点 final TextEditingController _controller1 = TextEditingController(); final TextEditingController _controller2 = TextEditingController(); final TextEditingController _controller3 = TextEditingController(); final FocusNode _focusNode1 = FocusNode(); final FocusNode _focusNode2 = FocusNode(); final FocusNode _focusNode3 = FocusNode(); // 当前激活的输入框控制器 TextEditingController? _activeController; @override void initState() { super.initState(); // 监听每个焦点节点的变化,更新激活的控制器 _focusNode1.addListener(_updateActiveController); _focusNode2.addListener(_updateActiveController); _focusNode3.addListener(_updateActiveController); } void _updateActiveController() { setState(() { if (_focusNode1.hasFocus) { _activeController = _controller1; } else if (_focusNode2.hasFocus) { _activeController = _controller2; } else if (_focusNode3.hasFocus) { _activeController = _controller3; } else { _activeController = null; } }); } // 退格键逻辑:删除当前激活控制器的最后一个字符 void _onDelete() { if (_activeController != null && _activeController!.text.isNotEmpty) { _activeController!.text = _activeController!.text.substring(0, _activeController!.text.length - 1); } } // 提交逻辑(根据需求自定义) void _onSubmit() { // 处理提交操作,比如获取所有输入框的值 print("输入1: ${_controller1.text}, 输入2: ${_controller2.text}, 输入3: ${_controller3.text}"); } @override void dispose() { // 销毁控制器和焦点节点,避免内存泄漏 _controller1.dispose(); _controller2.dispose(); _controller3.dispose(); _focusNode1.dispose(); _focusNode2.dispose(); _focusNode3.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("多输入框数字键盘")), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ // 第一个输入框 TextFormField( controller: _controller1, focusNode: _focusNode1, keyboardType: TextInputType.none, // 禁用系统键盘 decoration: const InputDecoration(labelText: "输入框1"), ), const SizedBox(height: 16), // 第二个输入框 TextFormField( controller: _controller2, focusNode: _focusNode2, keyboardType: TextInputType.none, decoration: const InputDecoration(labelText: "输入框2"), ), const SizedBox(height: 16), // 第三个输入框 TextFormField( controller: _controller3, focusNode: _focusNode3, keyboardType: TextInputType.none, decoration: const InputDecoration(labelText: "输入框3"), ), const SizedBox(height: 32), // 自定义数字键盘 NumPadCheckout( activeController: _activeController, delete: _onDelete, onSubmit: _onSubmit, ), ], ), ), ); } }
2. 修改自定义数字键盘组件
调整NumPadCheckout及其子组件,使其依赖当前激活的控制器,并修正原代码中的回调执行错误:
// 定义键盘按钮的宽高(根据你的需求调整) const double _keyWidth = 100; const double _keyHeight = 60; class NumPadCheckout extends StatelessWidget { final TextEditingController? activeController; final VoidCallback delete; final VoidCallback onSubmit; const NumPadCheckout({ super.key, required this.delete, required this.onSubmit, required this.activeController, }); @override Widget build(BuildContext context) { return Column( children: [ const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ NumberButtonCheckout(number: "1", controller: activeController), NumberButtonCheckout(number: "2", controller: activeController), NumberButtonCheckout(number: "3", controller: activeController), ], ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ NumberButtonCheckout(number: "4", controller: activeController), NumberButtonCheckout(number: "5", controller: activeController), NumberButtonCheckout(number: "6", controller: activeController), ], ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ NumberButtonCheckout(number: "7", controller: activeController), NumberButtonCheckout(number: "8", controller: activeController), NumberButtonCheckout(number: "9", controller: activeController), ], ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ NumberButtonCheckout(number: "00", controller: activeController), NumberButtonCheckout(number: "0", controller: activeController), CustomKey( iconData: Icons.keyboard_backspace, onPressed: delete, // 直接传递删除回调 ) ], ), ], ); } } class NumberButtonCheckout extends StatelessWidget { final String number; final TextEditingController? controller; const NumberButtonCheckout({ super.key, required this.number, required this.controller, }); @override Widget build(BuildContext context) { return SizedBox( width: _keyWidth, height: _keyHeight, child: ElevatedButton( onPressed: () { // 仅当控制器存在时追加数字 if (controller != null) { controller!.text += number; } }, child: Center(child: Text(number)), ), ); } } class CustomKey extends StatelessWidget { final IconData iconData; final VoidCallback? onPressed; const CustomKey({ super.key, required this.iconData, required this.onPressed, }); @override Widget build(BuildContext context) { return SizedBox( width: _keyWidth, height: _keyHeight, child: ElevatedButton( onPressed: onPressed, // 修正原代码的回调执行错误 child: Center(child: Icon(iconData)), ), ); } }
关键说明
- 焦点监听:通过
FocusNode的监听器,实时更新当前激活的TextEditingController,确保键盘操作指向正确的输入框。 - 组件解耦:修改后的数字键盘不再绑定固定控制器,而是接收当前激活的控制器,实现了多输入框复用。
- 错误修正:原代码中
CustomKey的onPressed存在执行错误(() => onPressed只是返回函数,未执行),现在直接传递回调函数即可触发操作。 - 内存管理:页面中必须销毁所有
TextEditingController和FocusNode,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Sameera Tennakoon
相关产品推荐
相关产品推荐

