Flutter中TextFormField点击后键盘弹出立即消失问题求助
聊天模块TextField点击后键盘弹出即消失,且initState重复调用问题
问题描述
开发聊天模块时,顶部用ListView展示对话内容,底部使用TextField发送消息。遇到以下问题:
- 点击TextField时,键盘弹出后立刻消失
- 点击TextField时
initState会被重新调用(已通过打印日志确认),该方法中包含Firestore数据流的监听 - 已尝试网络及ChatGPT提供的多种解决方案,均无效
关键观察
注释掉ListView.Builder所在的Expanded代码段后:
// Expanded( // child: _buildChatSection(), // ),
点击TextField仍然出现键盘弹出即消失的情况,说明问题根源在TextField本身。
完整代码
import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../constants/constant.dart'; import '../../controller/controller.dart'; import '../../models/feed_model.dart'; import '../../service/FirebaseService/form_service.dart'; class FormPage extends StatefulWidget { FormPage({super.key}); @override State<FormPage> createState() => _FormPageState(); } class _FormPageState extends State<FormPage> { final messagesCollection = FirebaseFirestore.instance.collection('Form'); StreamSubscription<QuerySnapshot>? _streamSubscription; List<DocumentSnapshot> messages = []; FormService fs = FormService(); TextEditingController textController = TextEditingController(); final FocusNode focus = FocusNode(); @override void initState() { super.initState(); _streamSubscription = messagesCollection .orderBy('createdDateTime') .snapshots() .listen((QuerySnapshot snapshot) { if (mounted) { setState(() { messages = snapshot.docs; }); } }); } Widget _buildChatSection() { return messages.isEmpty ? Center( child: text("No messages yet...", fontSize: Get.height * 0.03), ) : Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: ListView.builder( itemCount: messages.length, itemBuilder: (BuildContext context, int index) { FeedModel model = fs.getFeedModel(messages[index]); bool isCurrentUser = model.uid == Controller.uid; return Padding( padding: const EdgeInsets.only(top: 10.0), child: Align( alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft, child: Container( decoration: BoxDecoration( border: Border.all( color: isCurrentUser ? accentColor : primarySubtitleColor, ), borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ text('~${model.name}'), text(model.body!) ], ), )), ), ); }, ), ); } @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: _buildChatSection(), ), TextField( controller: textController, focusNode: focus, decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(30), borderSide: BorderSide( color: accentColor, ), ), ), ), ], ), ), ); } }
调试控制台输出
D/InputMethodManager(26484): showSoftInput() view=io.flutter.embedding.android.FlutterView{2a1a128 VFE...... .F...... 0,0-1080,2268 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT W/OnBackInvokedCallback(26484): OnBackInvokedCallback is not enabled for the application. W/OnBackInvokedCallback(26484): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest. D/InsetsController(26484): show(ime(), fromIme=true)
解决方案
1. 正确管理资源生命周期
当前代码未释放FocusNode、TextEditingController和StreamSubscription,可能导致焦点异常。添加dispose方法:
@override void dispose() { focus.dispose(); textController.dispose(); _streamSubscription?.cancel(); super.dispose(); }
2. 避免Widget重复重建
initState重复调用说明FormPage被频繁重建。检查父Widget是否在build方法中直接创建FormPage实例(如FormPage()),改为将FormPage实例保存在父Widget的状态中,或使用const构造(如果允许)。
3. 手动强制请求焦点
在TextField的onTap方法中主动请求焦点,避免焦点丢失:
TextField( controller: textController, focusNode: focus, onTap: () { if (!focus.hasFocus) { focus.requestFocus(); } }, decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(30), borderSide: BorderSide( color: accentColor, ), ), ), )
4. 改用StreamBuilder管理Firestore数据流
替换手动订阅和setState,使用StreamBuilder自动监听数据流,减少不必要的setState调用:
Widget _buildChatSection() { return StreamBuilder<QuerySnapshot>( stream: messagesCollection.orderBy('createdDateTime').snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: text('加载出错')); } if (!snapshot.hasData) { return Center(child: text('No messages yet...', fontSize: Get.height * 0.03)); } final messages = snapshot.data!.docs; return Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: ListView.builder( itemCount: messages.length, itemBuilder: (BuildContext context, int index) { FeedModel model = fs.getFeedModel(messages[index]); bool isCurrentUser = model.uid == Controller.uid; return Padding( padding: const EdgeInsets.only(top: 10.0), child: Align( alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft, child: Container( decoration: BoxDecoration( border: Border.all( color: isCurrentUser ? accentColor : primarySubtitleColor, ), borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ text('~${model.name}'), text(model.body!) ], ), )), ), ); }, ), ); }, ); }
同时删除initState中的手动订阅代码,无需再管理_streamSubscription和messages列表。
5. 配置AndroidManifest解决回调警告
按调试日志提示,在AndroidManifest.xml的application标签中添加配置,消除潜在交互异常:
android:enableOnBackInvokedCallback="true"
内容的提问来源于stack exchange,提问作者Shashank Deepak
相关产品推荐
相关产品推荐

