You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter BLOC聊天应用TextFormField无法输入问题求助

Flutter BLOC聊天应用输入框失效与消息显示问题解决方案

问题根源

  1. 输入框无法输入:ChatDetailsScreen的build方法中每次都会调用getMessages,导致Firestore的snapshots()被重复监听,消息更新时频繁emit(SocialGetMessageSuccessState())触发UI全量重建,打断TextFormField的InputConnection,引发getTextBeforeCursor on inactive InputConnection类报错。
  2. 切换聊天对象显示旧消息:全局共享的messages列表未区分不同聊天对象,切换用户时旧消息未被正确替换。
  3. 移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 20:20:33