Flutter中点击TextField后键盘立即收起的问题排查求助
问题:返回页面后TextField点击唤起键盘立即收起的异常
我有一个包含ListView和TextField的Flutter Widget,初始状态下点击TextField能正常唤起键盘;但当我输入内容后点击键盘外部收起键盘,再导航到其他页面并返回原页面时,再次点击TextField会出现键盘弹出后立即收起的异常情况。仅使用GestureDetector、ListView和TextField的情况下,可能是什么原因导致的?
以下是我的代码:
// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables import 'dart:math'; import 'package:flutter/cupertino.dart' as cupertino; import 'package:flutter/material.dart' hide BoxDecoration, BoxShadow; import 'package:flutter/services.dart'; import 'package:flutter_animator/flutter_animator.dart'; import 'package:flutter_inset_box_shadow/flutter_inset_box_shadow.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:iknowApp/constant.dart'; import 'package:animated_theme_switcher/animated_theme_switcher.dart' as theme; import 'package:like_button/like_button.dart'; import 'package:string_similarity/string_similarity.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../data/sentencesData.dart'; import '../data/sentencesDataEN.dart'; import '../data/sharedPreferences/sharedPreferences.dart'; import '../data/theme.dart'; import '../main.dart'; class QuizMode extends StatefulWidget { const QuizMode({ Key key, }) : super(key: key); @override State<QuizMode> createState() => _QuizModeState(); } class _QuizModeState extends State<QuizMode> { bool isPressed = false; var result; bool correct = true; int contador = 0; int quizScore = 0; final controller = TextEditingController(); var list = sentences; // generates a new Random object var _random = new Random(); var element; final formKey = GlobalKey(); FocusNode myFocusNode; bool check = true; bool like1 = true; bool like2 = true; bool like3 = true; @override void initState() { // TODO: implement initState super.initState(); element = list[_random.nextInt(list.length)]; myFocusNode = FocusNode(); } @override void dispose() { myFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { Offset distance = isPressed ? Offset(MediaQuery.of(context).size.height * 0.01, MediaQuery.of(context).size.height * 0.01) : Offset(MediaQuery.of(context).size.height * 0.03, MediaQuery.of(context).size.height * 0.03); double blur = isPressed ? 5 : 30; return !Constant.quizMode ? const MyHomePage() : theme.ThemeSwitchingArea( child: cupertino.GestureDetector( onTap: () { cupertino.FocusScope.of(context).unfocus(); }, child: Scaffold( body: SafeArea( child: cupertino.Center( child: ListView.builder( shrinkWrap: true, itemCount: 1, itemBuilder: (context, index) { final answ = element.answer[index]; final ques = element.question[index]; return cupertino.Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( height: contador < 3 ? MediaQuery.of(context).size.height * 0.1 : MediaQuery.of(context).size.height * 0.05, ), contador < 3 ? cupertino.Column( children: [ cupertino.Padding( padding: EdgeInsets.only( left: MediaQuery.of(context) .size .width * 0.1, right: MediaQuery.of(context) .size .width * 0.1), child: cupertino.Center( child: TextField( focusNode: myFocusNode, enabled: contador > 2 ? false : true, key: formKey, style: TextStyle( color: Constant.isDarkMode ? cupertino.Color.fromARGB( 255, 179, 29, 29) .withOpacity(0.7) : cupertino.Color.fromARGB( 255, 109, 4, 4), ), controller: controller, // onChanged: onSearchTextChanged, textAlign: TextAlign.left, onSubmitted: (value) async { print(result); if (value != "") { String answer = answ .toString() .toLowerCase(); result = controller.text .toString() .toLowerCase() .similarityTo(answer); if (result.toDouble() > 0.5) { quizScore++; if (quizScore > Constant.score) { Constant.score = quizScore; await DataPreferences .setScore( Constant.score); } setState(() { contador = 0; _random = Random(); element = list[ _random.nextInt( list.length)]; correct = true; controller.clear(); myFocusNode .requestFocus(); }); } else { setState(() { correct = false; contador++; HapticFeedback .heavyImpact(); if (contador == 1) { like3 = false; } if (contador == 2) { like2 = false; } if (contador == 3) { like1 = false; } }); } } }, decoration: InputDecoration( errorText: correct == false ? AppLocalizations.of( context) .incorrect : null, enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: Constant.isDarkMode ? cupertino.Color .fromARGB(255, 97, 55, 55) .withOpacity(0.7) : cupertino.Color .fromARGB( 255, 77, 6, 6), ), ), hintText: AppLocalizations.of(context) .answer, hintStyle: TextStyle( fontSize: MediaQuery.of(context) .size .height * 0.02, fontWeight: FontWeight.normal, color: Constant.isDarkMode ? cupertino.Color .fromARGB(255, 151, 111, 111) .withOpacity(0.7) : cupertino.Color .fromARGB( 255, 59, 31, 104), ), focusedBorder: InputBorder.none, ), ), ), ), ], ) : cupertino.Center(), theme.ThemeSwitcher(builder: (context) { return InkWell( onTap: () { setState(() { Constant.bodyAnimation = true; Constant.quizMode = false; }); }, child: Text(AppLocalizations.of(context).returnn, style: GoogleFonts.alike( textStyle: Theme.of(context) .textTheme .displayMedium, fontSize: MediaQuery.of(context) .size .width * 0.04, color: Constant.isDarkMode ? cupertino.Color.fromARGB( 255, 185, 51, 51) .withOpacity(0.7) : cupertino.Color.fromARGB( 255, 158, 158, 158), )), ); }), ], ), ); }, ), ), ), ), ), ); } }
分析与解决方案
这个问题我之前排查过类似的场景,核心原因和你页面里的GestureDetector以及FocusNode的状态管理有关,具体来说有这两点:
- GestureDetector的全局点击事件冲突:你用
GestureDetector包裹了整个页面,onTap里调用FocusScope.of(context).unfocus()来收起键盘。但当你从其他页面返回时,页面重建过程中这个GestureDetector的点击事件可能会被意外触发(比如手势识别的层级优先级问题),导致刚唤起的键盘立即被强制收起。 - FocusNode的状态未正确重置:页面返回后,
FocusNode可能残留着之前的失焦状态,或者在页面重建时没有正确重新关联焦点逻辑,导致唤起键盘的操作被打断。
针对你的代码,这里有几个具体的修复方案:
方案1:优化GestureDetector的行为,避免全局拦截
给GestureDetector添加behavior: HitTestBehavior.translucent属性,这样TextField的点击事件会优先被处理,不会被外层的GestureDetector拦截:
cupertino.GestureDetector( behavior: HitTestBehavior.translucent, // 添加这一行 onTap: () { cupertino.FocusScope.of(context).unfocus(); }, child: Scaffold(...), )
方案2:在页面返回时重置FocusNode状态
重写didChangeDependencies方法,在页面再次可见时重置FocusNode的状态,确保它能正常获取焦点:
@override void didChangeDependencies() { super.didChangeDependencies(); // 页面返回后,重置FocusNode的状态 WidgetsBinding.instance.addPostFrameCallback((_) { myFocusNode.unfocus(); }); }
额外优化:简化列表嵌套
你的ListView.builder设置了itemCount:1,其实完全可以用Column直接替代,减少不必要的列表嵌套,也能避免一些手势识别的层级冲突问题。
修改后测试一下:点击TextField唤起键盘,输入后点击外部收起,导航到其他页面再返回,再次点击TextField应该就能正常唤起键盘了。
内容的提问来源于stack exchange,提问作者Juan Sin Miedo
相关产品推荐
相关产品推荐

