Flutter调用Navigator.push后出现键盘闪现问题该如何解决
Flutter页面跳转键盘闪现问题解决方案
根本原因定位
完整触发链路如下:
- 点击空白区域收起键盘时,仅释放了TextField的输入焦点,但当前页面所在路由的
FocusScopeNode仍保留了最后持有焦点的TextField的上下文记录 - 执行
Navigator.push触发新路由入栈时,调用Route.didPush()方法会将焦点交回当前未被遮挡的旧路由的FocusScopeNode,该节点会自动将焦点恢复给之前绑定的TextField,触发键盘弹出 - 待新路由页面完全渲染覆盖旧页面后,旧页面的焦点被系统强制回收,键盘立刻收起,最终形成闪现效果
- 验证方法:给TextField绑定自定义
FocusNode并添加焦点变化监听,同时打印路由生命周期日志,可以观测到路由push后短时间内TextField先获得焦点又立刻丢失的时序,即可确认该根因。
有效规避方案
方案1:跳转前手动清除全局焦点(90%场景适用,改动最小)
跳转前主动释放所有持有焦点的节点,清除FocusScopeNode的残留记录即可,代码示例:
ElevatedButton( onTap: () async { // 主动释放全局焦点 FocusManager.instance.primaryFocus?.unfocus(); // 可选:添加50ms延迟等待焦点释放完全,低端机场景建议添加 await Future.delayed(const Duration(milliseconds: 50)); // 原有跳转逻辑 await Navigator.of(context).push( MaterialPageRoute( settings: // 原有配置 builder: // 原有页面构造 ) ); }, child: const Text('下一步') )
方案2:路由设置autofocus为false(无需修改焦点逻辑)
给MaterialPageRoute设置autofocus: false参数,禁用新路由入栈时的焦点自动回设逻辑:
await Navigator.of(context).push( MaterialPageRoute( autofocus: false, settings: // 原有配置 builder: // 原有页面构造 ) );
方案3:自定义路由全局禁用自动焦点(多页面批量生效场景适用)
如果多个页面都存在该问题,可自定义路由类重写didPush方法,移除默认的焦点请求逻辑:
class NoAutoFocusMaterialPageRoute<T> extends MaterialPageRoute<T> { NoAutoFocusMaterialPageRoute({ required super.builder, super.settings, super.maintainState = true, super.fullscreenDialog = false, }); @override TickerFuture didPush() { // 直接返回完成状态,不执行默认的requestFocus逻辑 return TickerFuture.complete(); } }
后续所有页面跳转统一使用该自定义路由类即可全局规避该问题。
内容的提问来源于stack exchange,提问作者anber
相关产品推荐
相关产品推荐

