Flutter TextField重聚焦光标位置异常:自定义失焦逻辑与KeyboardDismissOnTap差异
核心差异及原因
1. TextEditingController初始化位置错误
你两份代码都存在同一个问题:将TextEditingController的初始化逻辑写在了build方法中。StatelessWidget的build方法会在页面每次重绘时执行,每次都会生成全新的控制器实例,新实例默认的文本选择位置是文本开头偏移量0的位置。
你自行实现手势逻辑时,手势触发的失焦操作大概率触发了页面重绘,导致TextField被替换为新的控制器,之前点击位置对应的选择状态直接丢失,重聚焦时就会默认跳到开头。使用KeyboardDismissOnTap时测试场景下没有触发页面重绘,控制器没有被替换,状态得以保留,所以表现正常。
2. GestureDetector命中测试行为配置缺失
你自行实现的GestureDetector没有显式设置behavior属性,默认是HitTestBehavior.deferToChild,仅当点击到子组件(TextField、按钮等)时才会响应手势,点击页面空白区域时不会触发你写的失焦逻辑。
而官方flutter_keyboard_visibility包中的KeyboardDismissOnTap组件,内部的GestureDetector默认配置了HitTestBehavior.translucent,可以捕获空白区域的点击事件,事件传递逻辑和你自己实现的版本不一致,也是效果不同的原因之一。
3. 事件传递顺序冲突
当你点击TextField时,事件会从最内层组件向外层冒泡:首先TextField会处理点击,将光标定位到点击位置并申请焦点,之后事件传到外层的GestureDetector。如果你的GestureDetector配置了透传点击,会执行失焦逻辑,刚拿到焦点的TextField会立刻失焦,之后TextField自身的点击逻辑会再次触发聚焦,第二次聚焦时没有点击位置信息,就会默认跳到文本开头。
修复方案
将页面改为StatefulWidget,把TextEditingController作为State的持久化成员变量,同时给GestureDetector添加正确的命中测试行为,参考代码如下:
class Mypage extends StatefulWidget { const Mypage({Key? key}) : super(key: key); @override State<Mypage> createState() => _MypageState(); } class _MypageState extends State<Mypage> { late final TextEditingController _controller; @override void initState() { super.initState(); // 仅在初始化时创建一次控制器 _controller = TextEditingController(text: 'Existing Text Here'); } @override void dispose() { // 页面销毁时释放控制器 _controller.dispose(); super.dispose(); } void _hideKeyboard(BuildContext context) { final currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus && currentFocus.hasFocus) { FocusManager.instance.primaryFocus?.unfocus(); } } @override Widget build(BuildContext context) { return GestureDetector( // 配置命中行为,捕获空白区域点击,同时不拦截子组件的点击事件 behavior: HitTestBehavior.translucent, onTap: () => _hideKeyboard(context), child: Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: _controller, ), TextButton( onPressed: () => _hideKeyboard(context), child: const Text('Unfocus'), ) ], ), ), ), ); } }
内容的提问来源于stack exchange,提问作者satoru_02

