Flutter BLOC聊天应用TextFormField无法输入问题求助
Flutter BLOC聊天应用输入框失效与消息显示问题解决方案
问题根源
- 输入框无法输入:
ChatDetailsScreen的build方法中每次都会调用getMessages,导致Firestore的snapshots()被重复监听,消息更新时频繁emit(SocialGetMessageSuccessState())触发UI全量重建,打断TextFormField的InputConnection,引发getTextBeforeCursor on inactive InputConnection类报错。 - 切换聊天对象显示旧消息:全局共享的
messages列表未区分不同聊天对象,切换用户时旧消息未被正确替换。 - 移除emit后消息不更新:
emit是Bloc通知UI刷新的核心,移除后消息列表更新无法触发UI重建,导致旧消息不显示、切换用户无响应。
具体修复步骤
1. 将ChatDetailsScreen改为StatefulWidget,避免重复监听
把无状态组件改为有状态,在initState中仅调用一次getMessages,切换聊天对象时重新获取对应消息:
class ChatDetailsScreen extends StatefulWidget { final SocialUserModel userModel; const ChatDetailsScreen({required this.userModel, Key? key}) : super(key: key); @override State<ChatDetailsScreen> createState() => _ChatDetailsScreenState(); } class _ChatDetailsScreenState extends State<ChatDetailsScreen> { final messageController = TextEditingController(); String? currentReceiverId; @override void initState() { super.initState(); currentReceiverId = widget.userModel.uId; SocialCubit.get(context).getMessages(receiverId: currentReceiverId!); } @override void didUpdateWidget(covariant ChatDetailsScreen oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.userModel.uId != widget.userModel.uId) { currentReceiverId = widget.userModel.uId; SocialCubit.get(context).getMessages(receiverId: currentReceiverId!); } } @override void dispose() { messageController.dispose(); super.dispose(); } // 原build方法及内部widget代码保持不变 }
2. 用Map存储不同聊天对象的消息列表
替换Cubit中全局的messages列表,改为按接收者ID区分的Map,避免消息混淆:
// 替换原List<MessageModel> messages = []; Map<String, List<MessageModel>> messagesMap = {}; void getMessages({required String receiverId}) { FirebaseFirestore.instance .collection('users') .doc(userModel!.uId!) .collection('chats') .doc(receiverId) .collection('messages') .orderBy('dateTime') .snapshots() .listen((event) { List<MessageModel> messages = []; event.docs.forEach((element) { messages.add(MessageModel.fromJson(element.data())); }); messagesMap[receiverId] = messages; emit(SocialGetMessageSuccessState()); }); }
3. 修改聊天界面的消息获取逻辑
在ChatDetailsScreen的消息列表中,根据当前聊天对象的ID从Map中取对应消息:
Expanded( child: ListView.separated( physics: BouncingScrollPhysics(), itemBuilder: (context, index) { final targetMessages = SocialCubit.get(context).messagesMap[widget.userModel.uId!] ?? []; final message = targetMessages[index]; return SocialCubit.get(context).userModel!.uId == message.senderId ? buildMyMessage(message) : buildMessage(message); }, separatorBuilder: (context, index) => const SizedBox(height: 7), itemCount: SocialCubit.get(context).messagesMap[widget.userModel.uId!]?.length ?? 0, ), ),
4. 优化发送消息的emit逻辑
合并两次emit调用,避免不必要的UI重建:
void sendMessage({ required String receiverId, required String dateTime, required String text, }) { final model = MessageModel( text: text, senderId: userModel!.uId, receiverId: receiverId, dateTime: dateTime, ); Future.wait([ FirebaseFirestore.instance .collection('users') .doc(userModel!.uId) .collection('chats') .doc(receiverId) .collection('messages') .add(model.toMap()), FirebaseFirestore.instance .collection('users') .doc(receiverId) .collection('chats') .doc(userModel!.uId) .collection('messages') .add(model.toMap()), ]).then((_) { emit(SocialSendMessageSuccessState()); }).catchError((_) { emit(SocialSendMessageErrorState()); }); }
5. 发送成功后清空输入框
在BlocConsumer的listener中处理输入框清空:
BlocConsumer<SocialCubit,SocialStates>( listener: (context,state) { if (state is SocialSendMessageSuccessState) { messageController.clear(); } }, // 原builder代码保持不变 )
内容的提问来源于stack exchange,提问作者hazem saad
相关产品推荐
相关产品推荐

