Flutter中FocusScope的canRequestFocus变化时如何自动聚焦后代控件
Flutter 弹窗关闭后背景TextField无法自动恢复焦点问题解决
问题根因
你在setState回调中同步执行了隐藏弹窗和请求背景焦点的逻辑,此时背景层FocusScope的canRequestFocus属性还未完成更新,仍为false状态,导致焦点请求被直接拦截失效,等下一帧属性更新为true时,本次请求已经被丢弃,所以不会自动对焦。
修复方案
方案1:调整焦点请求时序(最小改动适配原有逻辑)
只需要修改_onPressPopup方法,把焦点请求放到页面更新完成的后置回调中执行即可:
void _onPressPopup() { setState(() { _show = false; }); // 等页面重绘完成、FocusScope属性更新后再请求焦点 WidgetsBinding.instance.addPostFrameCallback((_) { _normalFN.requestFocus(); }); }
方案2:使用系统焦点域管理(更规范无额外维护成本)
Flutter的FocusScope本身自带焦点栈管理能力,不需要手动维护焦点节点的切换,只需要给弹窗也套上FocusScope并开启自动对焦即可:
- 弹窗组件外层新增
FocusScope配置:
class Popup extends StatelessWidget { const Popup(this.onPress, {Key? key}) : super(key: key); final void Function() onPress; @override Widget build(BuildContext context) { return FocusScope( autofocus: true, child: Container( color: Colors.yellow, width: 300, child: Column( mainAxisSize: MainAxisSize.min, children: [ const TextField(), const SizedBox(height: 30), ElevatedButton(onPressed: onPress, child: const Text('close')) ], ), ), ); } }
- 简化状态层逻辑,移除不需要的手动焦点控制代码:
class _MyHomePageState extends State<MyHomePage> { var _show = false; final _normalFN = FocusNode(); void _onPressNormal() { setState(() { _show = true; }); } void _onPressPopup() { setState(() { _show = false; }); } @override void dispose() { _normalFN.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: Stack( alignment: Alignment.center, children: [ FocusScope( canRequestFocus: !_show, child: Column(children: [ TextField(focusNode: _normalFN, autofocus: true), const SizedBox(height: 20), ElevatedButton( onPressed: _onPressNormal, child: const Text("open popUp")), ])), if (_show) Popup(_onPressPopup), ], ), ); } }
这种方案下,弹窗显示时会自动抢占焦点,背景层FocusScope禁止焦点请求保证不会被后台控件抢焦,弹窗销毁时系统会自动把焦点切回之前的焦点域,不需要手动处理任何焦点切换逻辑,也不会出现时序问题。
内容的提问来源于stack exchange,提问作者Midhunraj R Pillai
相关产品推荐
相关产品推荐

