如何解决FocusNode实现后返回箭头导航引发的异常?
问题分析与解决方案
问题根源
点击TextField后返回主页面时,_onFocusChange方法里的Future.delayed会在页面的State已被销毁后,仍尝试执行setState操作。这会触发Flutter框架的未捕获异常——因为不允许在已销毁的State实例上调用setState,最终导致主页面UI冻结、无法响应交互。
返回操作会立刻销毁MyHomePage的State,但延迟500ms的回调还在事件队列中,当回调执行时,State已经不存在,此时的setState调用会破坏UI状态一致性。
修复方案
方案1:执行setState前检查挂载状态
修改_onFocusChange方法,在更新状态前先判断当前State是否仍处于挂载状态:
void _onFocusChange() { Future.delayed(const Duration(milliseconds: 500), () { // 仅在State未销毁时执行状态更新 if (mounted) { setState(() { heightKeyboard = MediaQuery.of(context).viewInsets.bottom; setDropdownMenuHeight(heightAppBar, heightTextField, heightKeyboard); }); } }); }
方案2:用可取消任务管理延迟操作
借助async包的CancelableOperation,在页面销毁时主动取消未执行的延迟任务:
- 先在
pubspec.yaml添加依赖:
dependencies: flutter: sdk: flutter async: ^2.11.0 # 可使用最新版本
- 修改MyHomePage的State代码:
import 'package:async/async.dart'; class _MyHomePageState extends State<MyHomePage> { // ... 其他变量 CancelableOperation? _focusDelayTask; void _onFocusChange() { // 取消之前未完成的任务,避免重复执行 _focusDelayTask?.cancel(); _focusDelayTask = CancelableOperation.fromFuture( Future.delayed(const Duration(milliseconds: 500), () { setState(() { heightKeyboard = MediaQuery.of(context).viewInsets.bottom; setDropdownMenuHeight(heightAppBar, heightTextField, heightKeyboard); }); }), ); } @override void dispose() { _focusDelayTask?.cancel(); // 销毁前取消延迟任务 _focusNode.unfocus(); // 主动取消焦点,清理键盘相关状态 _focusNode.removeListener(_onFocusChange); _focusNode.dispose(); firstController.dispose(); super.dispose(); } // ... 其他代码 }
验证步骤
修改代码后按以下流程测试:
- 点击主页面TextButton进入MyHomePage
- 点击TextField唤起键盘
- 点击返回箭头回到主页面
- 再次点击TextButton,确认可正常跳转,UI无冻结
内容的提问来源于stack exchange,提问作者rusty
相关产品推荐
相关产品推荐

