Flutter键盘收起后TextField仍聚焦、关闭抽屉键盘自动弹出如何解决
问题根因
按下实体返回键收起键盘时,系统仅隐藏了键盘的输入UI,没有同步释放TextField持有的焦点。
Flutter的侧边抽屉本质是压在主页面上的独立模态路由,打开再关闭抽屉的过程,会触发主页面路由重新获得焦点;此时TextField还处于聚焦状态,焦点系统会自动为其重新唤起键盘,就会出现关抽屉自动弹键盘的异常。
修复方法
两种方案都可以彻底解决问题,按需选择即可:
方案1:全局路由监听(适合多输入框、多页面的项目)
全局监听路由状态变化,在页面切后台、模态路由(抽屉、弹窗)关闭回到当前页时,主动清空全局焦点:
- 给项目根
MaterialApp注册路由观察者:
// 全局定义路由观察者,可放公共文件 final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>(); void main() { runApp(MaterialApp( navigatorObservers: [routeObserver], // 注册观察者 // 其他原有配置,比如theme、home等 )); }
- 在使用TextField的页面State中,混入路由监听逻辑,页面重新可见时清空焦点:
class _SearchPageState extends State<SearchPage> with RouteAware, WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void didChangeDependencies() { super.didChangeDependencies(); // 订阅路由事件 routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute); } @override void didPopNext() { // 从其他路由(抽屉、其他页面)回到当前页时,清空焦点 FocusManager.instance.primaryFocus?.unfocus(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { // 应用切后台、切前台时清空焦点,避免其他场景下键盘异常弹出 if (state == AppLifecycleState.inactive) { FocusManager.instance.primaryFocus?.unfocus(); } super.didChangeAppLifecycleState(state); } @override void dispose() { routeObserver.unsubscribe(this); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override Widget build(BuildContext context) { // 原有页面代码,你写的SizedBox+TextField直接放这里就行 return Scaffold( // ... ); } }
方案2:轻量局部处理(适合单页面、只有一个搜索框的场景)
不需要改全局配置,直接在当前页面处理返回键和点击失焦逻辑即可:
@override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { // 按下实体返回键时,如果输入框处于聚焦状态,先失焦关键盘,不触发页面返回 final hasFocus = FocusManager.instance.primaryFocus?.hasFocus ?? false; if (hasFocus) { FocusManager.instance.primaryFocus?.unfocus(); return false; } return true; }, child: GestureDetector( onTap: () { // 点击页面空白区域时,主动清空输入框焦点 FocusManager.instance.primaryFocus?.unfocus(); }, child: Scaffold( // 原有页面代码,包括drawer、appBar、body里的搜索框都放这里 body: Padding( padding: const EdgeInsets.all(16), child: SizedBox( height: 40, child: TextField( onChanged: (val) { print(val); }, decoration: InputDecoration( contentPadding: EdgeInsets.all(8), hintText:AppLocalizations.of(context)!.searchnameoremail, fillColor: Theme.of(context).dialogBackgroundColor, filled: true, border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: const BorderRadius.all(Radius.circular(10)), ), suffixIcon: Padding( padding: const EdgeInsets.all(defaultPadding - 2), child: SvgPicture.asset("assets/icons/Search.svg", color: Theme.of(context).dialogBackgroundColor), ) ), ), ), ), ), ), ); }
注意事项
- 优先用
FocusManager.instance.primaryFocus?.unfocus()清空焦点,不需要单独给TextField定义FocusNode,多输入框场景也不会漏处理。 - 不要只在TextField的onTap或者键盘关闭回调里处理焦点,系统返回键关键盘不会触发这些回调,会出现逻辑遗漏。
内容的提问来源于stack exchange,提问作者appdev
相关产品推荐
相关产品推荐

