Flutter:自定义键盘绑定TextField,如何在光标位置插入文本?
问题
我开发了一个固定在屏幕上的自定义数字键盘,绑定到一个通过指定keyboardType: TextInputType.none实现只读的TextField。当前问题是:当将光标移动到TextField内的任意位置后,点击自定义键盘输入数字,内容总是被追加到字符串末尾,且光标会自动回到字段开头。请问该如何修复这个问题?
相关代码
CustomTextField 实现
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class CustomTextField extends StatelessWidget { var w; var limit; var textFieldHint; var minL; var maxL; var cont; var kbtype; var dispCursor; CustomTextField( {Key? key, required this.w, required this.limit, required this.textFieldHint, required this.minL, required this.maxL, required this.cont, required this.kbtype, required this.dispCursor}) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 10.0), child: SizedBox( width: MediaQuery.of(context).size.width * w, child: TextField( showCursor: dispCursor, controller: cont, keyboardType: kbtype, decoration: InputDecoration( fillColor: Colors.white, filled: true, hintText: textFieldHint, hintStyle: TextStyle(fontSize: 18, color: Colors.grey), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none)), inputFormatters: [ LengthLimitingTextInputFormatter(limit), ], minLines: minL, maxLines: maxL, )), ); } }
CustomButton 实现
class CustomButton extends StatelessWidget { final int number; final TextEditingController controller; const CustomButton({ Key? key, required this.number, required this.controller, }) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 5.0, right: 3), child: ElevatedButton( onPressed: () { controller.text += number.toString(); }, child: Text( number.toString(), style: TextStyle(color: Colors.blueGrey), ), style: ButtonStyle( elevation: MaterialStateProperty.all(10), shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), fixedSize: MaterialStateProperty.all(Size.square(40)), backgroundColor: MaterialStateProperty.all(Colors.white)), ), ); } }
使用代码片段
import 'package:expensetracker/Screens/home.dart'; import 'package:expensetracker/Widgets/customButton.dart'; import 'package:expensetracker/Widgets/customDate.dart'; import 'package:expensetracker/Widgets/customTime.dart'; import 'package:expensetracker/Widgets/customTextField.dart'; import 'package:flutter/material.dart'; class AddScreen extends StatefulWidget { const AddScreen({Key? key}) : super(key: key); @override State<AddScreen> createState() => _AddScreenState(); } var choice = [textConst.income, textConst.expense]; var dropdownvalue = textConst.income; late DateTime _selectedDate; TextEditingController title = TextEditingController(); TextEditingController descrip = TextEditingController(); TextEditingController amount = TextEditingController(); TextEditingController date = TextEditingController(); TextEditingController time = TextEditingController(); class _AddScreenState extends State<AddScreen> { @override Widget build(BuildContext context) { return Scaffold(body: ... ...CustomTextField( dispCursor: true, kbtype: TextInputType.none, cont: amount, w: .8, limit: null, textFieldHint: textConst.amount, minL: 1, maxL: 1), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CustomButton( number: 1, controller: amount, ), CustomButton( number: 2, controller: amount, ), CustomButton( number: 3, controller: amount, ) ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CustomButton(number: 4, controller: amount), CustomButton(number: 5, controller: amount), CustomButton( number: 6, controller: amount, ), ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CustomButton( number: 7, controller: amount, ), CustomButton( number: 8, controller: amount, ), CustomButton( number: 9, controller: amount, ) ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CustomButton( number: 0, controller: amount, ), ElevatedButton( onPressed: () { amount.text = amount.text.substring(0, amount.text.length - 1); }, child: Text(textConst.delete), style: ButtonStyle( elevation: MaterialStateProperty.all(10), shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), minimumSize: MaterialStateProperty.all( Size(120, 40), ), backgroundColor: MaterialStateProperty.all(Colors.red), )) ], )
修复方案
问题根源
直接通过controller.text += ...修改文本时,会完全替换原有内容,导致系统丢失当前光标位置,默认将光标重置到文本开头,同时只能将内容追加到末尾。正确的做法是利用TextEditingController的selection属性获取当前光标/选中范围,在对应位置插入内容并更新光标位置。
具体修改
1. 修复CustomButton的输入逻辑
修改CustomButton中的onPressed方法,实现光标位置插入文本并更新光标:
onPressed: () { final currentText = controller.text; final currentSelection = controller.selection; // 替换选中内容(如果有选中)或在光标位置插入文本 final newText = currentText.replaceRange( currentSelection.start, currentSelection.end, number.toString(), ); controller.text = newText; // 将光标移动到插入文本的末尾 controller.selection = TextSelection( baseOffset: currentSelection.start + number.toString().length, extentOffset: currentSelection.start + number.toString().length, ); },
2. 修复删除按钮的逻辑
修改删除按钮的onPressed方法,支持删除选中内容或光标前的字符,并保持光标位置正确:
onPressed: () { final currentText = amount.text; final currentSelection = amount.selection; if (currentSelection.start == currentSelection.end) { // 无选中内容,删除光标前的字符 if (currentSelection.start > 0) { final newText = currentText.replaceRange( currentSelection.start - 1, currentSelection.start, '', ); amount.text = newText; // 光标前移一位 amount.selection = TextSelection( baseOffset: currentSelection.start - 1, extentOffset: currentSelection.start - 1, ); } } else { // 有选中内容,删除选中部分 final newText = currentText.replaceRange( currentSelection.start, currentSelection.end, '', ); amount.text = newText; // 光标停在删除位置的起始处 amount.selection = TextSelection( baseOffset: currentSelection.start, extentOffset: currentSelection.start, ); } },
3. 确保TextField支持光标交互
在CustomTextField的TextField组件中添加enableInteractiveSelection: true(默认已开启,显式声明避免意外配置):
TextField( showCursor: dispCursor, controller: cont, keyboardType: kbtype, enableInteractiveSelection: true, // 确保可移动光标和选中文本 // 原有配置保持不变... )
修改效果
- 点击自定义键盘数字时,文本会插入到当前光标位置,光标自动移动到插入内容的末尾
- 支持选中文本并替换为数字
- 删除操作支持删除选中内容或光标前的字符,光标位置保持合理
内容的提问来源于stack exchange,提问作者Abdul Karim
相关产品推荐
相关产品推荐

