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

Flutter Firebase开发中toDate()空调用报错如何解决

问题报错

运行Flutter+Firebase开发的VIP聊天应用真机调试时,Widgets库抛出如下NoSuchMethodError异常:

======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building:
The method 'toDate' was called on null.
Receiver: null
Tried calling: toDate()

关联代码

首页 HomePage 实现

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:vip_chat/chat_room.dart';
import 'package:vip_chat/fireBaseHelper.dart';
import 'package:vip_chat/model/chatRoom_model.dart';
import 'package:vip_chat/search_page.dart';

import 'model/messages_model.dart';
import 'model/user_model.dart';

class HomePage extends StatefulWidget {
  final userModel usermodel;
  final User firebasebaseUser;

  const HomePage({super.key, required this.usermodel, required this.firebasebaseUser});


  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text('VIP Chat App'),
      ),body: SafeArea(
      child: Container(
        child: StreamBuilder(
          stream: FirebaseFirestore.instance.collection('chatrooms').where('participants.${widget.usermodel.uid}',isEqualTo: true).snapshots(),
          builder: (context,snapshot){
            if(snapshot.connectionState==ConnectionState.active){
              if(snapshot.hasData){
                QuerySnapshot dataSnapshot=snapshot.data as QuerySnapshot;
                return ListView.builder(
                  itemCount: dataSnapshot.docs.length,itemBuilder: (context,index){
                    // 报错直接触发点
                    MessageModel currentMessage=MessageModel.fromMap(dataSnapshot.docs[index].data() as Map<String,dynamic>);
                    ChatRoomModel chatroommodel=ChatRoomModel.fromMap(dataSnapshot.docs[index].data()as Map<String,dynamic>);
                    Map<String,dynamic>participants=chatroommodel.participants!;
                    List<String>partkeys=participants.keys.toList();
                    partkeys.remove(widget.usermodel.uid);
                    return FutureBuilder(future: FireBaseHelper.getUsermodel(partkeys[0]),builder: (context,userData){

                      if(userData.connectionState==ConnectionState.done){
                        if(userData.data!=null){
                          // 附带类型强转bug位置
                          userModel targetUser=userData as userModel;
                          return ListTile(
                            onTap: (){
                              Navigator.push(context, MaterialPageRoute(builder: (context){
                                return ChatRoomPage(targetUser: targetUser, chatroom: chatroommodel, usermodel: widget.usermodel, firebaseuser: widget.firebasebaseUser);
                              }));
                            },
                            title: Text(targetUser.fullName.toString()),
                            subtitle: Text(chatroommodel.lastMessage.toString()),
                            leading: CircleAvatar(backgroundImage: NetworkImage(targetUser.profilepic.toString()),),

                          );
                        }
                        else{
                          return Container();
                        }

                      }else{
                        return Container();
                      }

                    },);
                },);
              }
              else if(snapshot.hasError){
                return Center(child: Text('An error occurred!'));
              }
              else{
                return Center(
                  child: Text('No chats'),
                );
              }
            }else{
              return Center(child: CircularProgressIndicator(),);
            }
          },
        ),
      ),
    ),
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          Navigator.push(context, MaterialPageRoute(builder: (context){return SearchPage(usermodel: widget.usermodel, firebaseuser:widget.firebaseuser );}));
        },
        child: Icon(Icons.search),
      ),
    );

  }
}

MessageModel 消息模型实现

import 'package:flutter/material.dart';

class MessageModel{
  String?messageid;
  String? sender;
  String?text;
  bool?seen;
  DateTime?createdon;

  MessageModel({this.sender,this.text,this.seen,this.createdon,this.messageid});

  MessageModel.fromMap(Map<String,dynamic>map){
    messageid=map['messageid'];
    sender=map['sender'];
    text=map['text'];
    seen=map['seen'];
    // 报错根源位置,无空判断
    createdon=map['createdon'].toDate();


  }

  Map<String,dynamic>toMap(){
    return{
      'messageid':messageid,
      'sender':sender,
      'text':text,
      'seen':seen,
      'createdon':createdon

    };
  }

}

ChatRoomPage 聊天室页面实现

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:vip_chat/main.dart';
import 'package:vip_chat/model/chatRoom_model.dart';
import 'package:vip_chat/model/messages_model.dart';

import 'model/user_model.dart';
class ChatRoomPage extends StatefulWidget {
  final userModel targetUser;
  final ChatRoomModel chatroom;
  final userModel usermodel;
  final User firebaseuser;

  const ChatRoomPage({super.key, required this.targetUser, required this.chatroom, required this.usermodel, required this.firebaseuser});



  @override
  State<ChatRoomPage> createState() => _ChatRoomPageState();
}

class _ChatRoomPageState extends State<ChatRoomPage> {
  TextEditingController messagecontroller=TextEditingController();
  void sendMessage() async{
    String msg=messagecontroller.text.trim();
    messagecontroller.clear();
    if(msg!=''){
      MessageModel newMessage=MessageModel(
          messageid: uuid.v1(),
          sender: widget.usermodel.uid,
          createdon: DateTime.now(),
          text: msg,
          seen: false
      );
      FirebaseFirestore.instance.collection('chatrooms').doc(widget.chatroom.chatroomid).collection('messages').doc(newMessage.messageid).set(newMessage.toMap());
      print('Message Sent');
      widget.chatroom.lastMessage=msg;
      FirebaseFirestore.instance.collection('chatrooms').doc(widget.chatroom.chatroomid).set(widget.chatroom.toMap());
      print('last message created');
    }
  }
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar:AppBar(
        title: Row(children: [
          CircleAvatar(backgroundImage: NetworkImage(widget.targetUser.profilepic.toString()),
          ),
          SizedBox(width: 10,),
          Text(widget.targetUser.fullName.toString())
        ],),
      ),
      body: SafeArea(child: Container(

        child: Column(children: [Expanded(child: Container(

          child: StreamBuilder(
            stream: FirebaseFirestore.instance.collection('chatrooms').doc(widget.chatroom.chatroomid).collection('messages').orderBy('createdon',descending: true).snapshots(),
            builder: (context,snapshot){
              if(snapshot.connectionState==ConnectionState.active){
                if(snapshot.hasData){
                  QuerySnapshot dataSnapshot=snapshot.data as QuerySnapshot;
                  return ListView.builder(reverse: true,
                    itemCount: dataSnapshot.docs.length,itemBuilder: (context,index){
                      MessageModel currentMessage=MessageModel.fromMap(dataSnapshot.docs[index].data() as Map<String,dynamic>);
                      return Padding(
                        padding: EdgeInsets.symmetric(horizontal: 10,vertical: 2),
                        child: Row(
                          mainAxisAlignment: currentMessage.sender==widget.usermodel.uid?MainAxisAlignment.end:MainAxisAlignment.start,
                          children: [
                            Container(
                                margin: EdgeInsets.symmetric(vertical: 2),
                                decoration:BoxDecoration(
                                    borderRadius: BorderRadius.circular(10),
                                    color: currentMessage.sender==widget.usermodel.uid?Colors.lightGreenAccent:Colors.orange
                                ),child: Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Text(currentMessage.text.toString(),style: TextStyle(fontSize: 15),),
                            )),
                          ],
                        ),
                      );
                    },);
                }
                else if(snapshot.hasError){
                  return Center(child: Text('An error occurred!'));
                }
                else{
                  return Center(
                    child: Text('say hi to your new friend'),
                  );
                }
              }else{
                return Center(child: CircularProgressIndicator(),);
              }
            },
          ),
        )),
        Padding(
          padding: EdgeInsets.symmetric(horizontal: 10,vertical: 10),
          child: Container(color: Colors.grey.shade200,
            child: Row(
              children: [Flexible(child: TextField(
                controller:messagecontroller,
                maxLines: null,
                decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: 'Enter your message here',
                ),
              )),
                IconButton(onPressed: (){sendMessage();}, icon: Icon(Icons.send),color: Colors.red,),
              ],
            ),),
        )],),

      ),),);
  }
}
错误根因
  1. 首页StreamBuilder拉取的是chatrooms根集合的文档数据,这类文档存储聊天室基础信息、参与者、最后一条消息内容,不存在createdon字段,但代码多余地将chatrooms根文档数据传入MessageModel.fromMap()解析,取map['createdon']时拿到null,直接调用toDate()触发空指针异常。且解析出来的currentMessage变量在首页完全没有被使用,属于无效代码。
  2. MessageModel.fromMap方法没有对createdon字段做空安全校验,后续拉取messages子集合数据时,如果遇到字段缺失的脏数据,同样会触发同类报错。
  3. 首页FutureBuilder中存在类型强转错误:直接将AsyncSnapshot类型的userData强转为userModel,会触发类型转换异常,属于附带代码bug。
修复步骤
  • 删除首页ListView.builder中无效的MessageModel解析代码,即删除MessageModel currentMessage=MessageModel.fromMap(dataSnapshot.docs[index].data() as Map<String,dynamic>);这一行,直接消除首页的报错触发点。
  • 修改MessageModel的fromMap方法,增加空判断逻辑,兼容字段缺失场景:
MessageModel.fromMap(Map<String,dynamic>map){
  messageid=map['messageid'];
  sender=map['sender'];
  text=map['text'];
  seen=map['seen'];
  // 空安全处理:字段非空时再调用toDate,否则赋值null
  createdon = map['createdon'] != null ? (map['createdon'] as Timestamp).toDate() : null;
}
  • 修正首页FutureBuilder中的类型强转错误,将userModel targetUser=userData as userModel;改为:
userModel targetUser = userData.data as userModel;
  • 打开Firebase控制台检查chatrooms集合、chatrooms/{chatroomid}/messages子集合下的测试数据,清理手动创建的缺失必要字段的脏文档,避免历史数据触发异常。

内容的提问来源于stack exchange,提问作者Vipin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:27:04