Flutter未处理异常:无效参数:'User'实例
问题描述
我是一名初学者,跟着一门旧课程开发聊天APP,用Firebase_auth和FirebaseFirestore实现登录注册,这俩功能正常。但登录后进入聊天界面,点发送按钮消息发不出去,还报异常。
运行时错误日志
I/TextInputPlugin(29334): Composing region changed by the framework. Restarting the input method. W/IInputConnectionWrapper(29334): beginBatchEdit on inactive InputConnection W/IInputConnectionWrapper(29334): getTextBeforeCursor on inactive InputConnection W/IInputConnectionWrapper(29334): getTextAfterCursor on inactive InputConnection W/IInputConnectionWrapper(29334): getSelectedText on inactive InputConnection W/IInputConnectionWrapper(29334): endBatchEdit on inactive InputConnection E/flutter (29334): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument: Instance of 'User' E/flutter (29334): #0 StandardMessageCodec.writeValue (package:flutter/src/services/message_codecs.dart:466:7) E/flutter (29334): #1 FirestoreMessageCodec.writeValue (package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart:119:13) E/flutter (29334): #2 StandardMessageCodec.writeValue.<anonymous closure> (package:flutter/src/services/message_codecs.dart:463:9) E/flutter (29334): #3 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13) E/flutter (29334): #4 StandardMessageCodec.writeValue (package:flutter/src/services/message_codecs.dart:461:13) E/flutter (29334): #5 FirestoreMessageCodec.writeValue (package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart:119:13) E/flutter (29334): #6 StandardMessageCodec.writeValue.<anonymous closure> (package:flutter/src/services/message_codecs.dart:463:9) E/flutter (29334): #7 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13) E/flutter (29334): #8 StandardMessageCodec.writeValue (package:flutter/src/services/message_codecs.dart:461:13) E/flutter (29334): #9 FirestoreMessageCodec.writeValue (package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart:119:13) E/flutter (29334): #10 StandardMethodCodec.encodeMethodCall (package:flutter/src/services/message_codecs.dart:604:18) E/flutter (29334): #11 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:285:34) E/flutter (29334): #12 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:472:12) E/flutter (29334): #13 MethodChannelDocumentReference.set (package:cloud_firestore_platform_interface/src/method_channel/method_channel_document_reference.dart:32:52) E/flutter (29334): #14 _JsonDocumentReference.set (package:cloud_firestore/src/document_reference.dart:166:22) E/flutter (29334): #15 _JsonCollectionReference.add (package:cloud_firestore/src/collection_reference.dart:109:23) E/flutter (29334): #16 _ChatScreenState.build.<anonymous closure> (package:flash_chat_flutter/screens/chat_screen.dart:86:73) E/flutter (29334): #17 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21) E/flutter (29334): #18 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24) E/flutter (29334): #19 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11) E/flutter (29334): #20 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5) E/flutter (29334): #21 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:239:7) E/flutter (29334): #22 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:615:9) E/flutter (29334): #23 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12) E/flutter (29334): #24 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9) E/flutter (29334): #25 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13) E/flutter (29334): #26 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18) E/flutter (29334): #27 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7) E/flutter (29334): #28 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:460:19) E/flutter (29334): #29 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22) E/flutter (29334): #30 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11) E/flutter (29334): #31 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7) E/flutter (29334): #32 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5) E/flutter (29334): #33 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7) E/flutter (29334): #34 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7) E/flutter (29334): #35 _invoke1 (dart:ui/hooks.dart:167:13) E/flutter (29334): #36 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7) E/flutter (29334): #37 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31) E/flutter (29334): D/InsetsController(29334): show(ime(), fromIme=true)
调试模式日志
I/TextInputPlugin(29642): Composing region changed by the framework. Restarting the input method. W/IInputConnectionWrapper(29642): beginBatchEdit on inactive InputConnection W/IInputConnectionWrapper(29642): getTextBeforeCursor on inactive InputConnection W/IInputConnectionWrapper(29642): getTextAfterCursor on inactive InputConnection W/IInputConnectionWrapper(29642): getSelectedText on inactive InputConnection W/IInputConnectionWrapper(29642): endBatchEdit on inactive InputConnection D/InsetsController(29642): show(ime(), fromIme=true)
相关代码(chat_screen.dart)
import 'package:flash_chat_flutter/screens/welcome_screen.dart'; import 'package:flutter/material.dart'; import 'package:flash_chat_flutter/constants.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; // late FirebaseAuth loggedInUser; User? loggedInUser = FirebaseAuth.instance.currentUser; class ChatScreen extends StatefulWidget { const ChatScreen({super.key}); static String id = 'chat_screen'; @override State<ChatScreen> createState() => _ChatScreenState(); } class _ChatScreenState extends State<ChatScreen> { final messageTextController = TextEditingController(); var messageText = ''; @override void initState() { getCurrentUser(); super.initState(); } User? getCurrentUser() { try { // final user = FirebaseAuth.instance.currentUser; if (loggedInUser != null) { return loggedInUser; } } catch (e) { print(e); } return null; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: null, actions: <Widget>[ IconButton( icon: const Icon(Icons.close), onPressed: () { FirebaseAuth.instance.signOut(); if (mounted) return; Navigator.pop(context, WelcomeScreen.id); }), ], title: const Text('⚡️Chat'), backgroundColor: Colors.lightBlueAccent, ), body: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ const Expanded( child: MessagesStream(), ), Container( decoration: kMessageContainerDecoration, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Expanded( child: TextField( controller: messageTextController, onChanged: (value) { setState(() { messageText = value; }); }, decoration: kMessageTextFieldDecoration, ), ), TextButton( onPressed: () { print(loggedInUser); FirebaseFirestore.instance.collection('messages').add( { 'text': messageText, 'sender': loggedInUser, }, ); messageTextController.clear(); }, child: const Text( 'Send', style: kSendButtonTextStyle, ), ), ], ), ), ], ), ), ); } } class MessagesStream extends StatelessWidget { const MessagesStream({super.key}); @override Widget build(BuildContext context) { return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance.collection('messages').snapshots(), builder: (context, snapshot) { // if (!snapshot.hasData) { // return const Center( // child: CircularProgressIndicator( // backgroundColor: Colors.lightBlueAccent, // ), // ); // } if (snapshot.hasError) { return const Center( child: Text('Something were wrong!'), ); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( backgroundColor: Colors.lightBlueAccent, ), ); } final messages = snapshot.data!.docs.reversed; List<MessageBubble> messageBubbles = []; if (messages != null) { for (var message in messages) { final messageText = message['text']; final messageSender = message['sender']; var currentUser = loggedInUser; final messageBubble = MessageBubble( sender: messageSender, text: messageText, isMe: currentUser == messageSender, ); messageBubbles.add(messageBubble); } } return ListView( reverse: true, padding: const EdgeInsets.symmetric( horizontal: 10.0, vertical: 20.0, ), children: messageBubbles, ); }, ); } } class MessageBubble extends StatelessWidget { const MessageBubble({ super.key, required this.sender, required this.text, required this.isMe, }); final String sender; final String text; final bool isMe; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Text( sender, style: const TextStyle( fontSize: 12.0, color: Colors.black54, ), ), Material( borderRadius: isMe ? const BorderRadius.only( topLeft: Radius.circular(30.0), bottomLeft: Radius.circular(30.0), bottomRight: Radius.circular(30.0), ) : const BorderRadius.only( topRight: Radius.circular(30.0), bottomRight: Radius.circular(30.0), bottomLeft: Radius.circular(30.0), ), elevation: 5.0, color: isMe ? Colors.lightBlueAccent : Colors.white, child: Padding( padding: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 20.0, ), child: Text( text, style: TextStyle( color: isMe ? Colors.white : Colors.black54, fontSize: 15.0, ), ), ), ), ], ), ); } }
解决方案
问题根源
错误日志里的Invalid argument: Instance of 'User'已经明确定位问题:你在向Firestore写入数据时,把整个User对象当成值传给了sender字段,但Firestore只能存储基本数据类型(字符串、数字、布尔等)或可序列化的对象,User实例无法直接序列化存储。
另外还有两个衍生问题:
- 后续判断
isMe时,用User对象和从Firestore读取的字符串对比,永远不会相等,导致气泡样式判断错误 MessageBubble里的sender字段定义为String,但传入的是User对象,会引发类型不匹配错误
修复步骤
1. 修复消息发送逻辑
在发送消息时,不要传loggedInUser,而是传用户的邮箱(或UID)这类字符串值:
TextButton( onPressed: () { print(loggedInUser); FirebaseFirestore.instance.collection('messages').add( { 'text': messageText, // 替换成用户邮箱或UID 'sender': loggedInUser!.email, // 或者用UID:loggedInUser!.uid }, ); messageTextController.clear(); }, child: const Text('Send', style: kSendButtonTextStyle), ),
2. 修复消息流中的对比逻辑
在MessagesStream里,把当前用户的邮箱(或UID)和从Firestore读取的sender字符串对比:
for (var message in messages) { final messageText = message['text']; final messageSender = message['sender']; // 用邮箱或UID对比 final currentUserEmail = loggedInUser!.email; final messageBubble = MessageBubble( sender: messageSender, text: messageText, isMe: currentUserEmail == messageSender, ); messageBubbles.add(messageBubble); }
3. (可选)优化用户获取逻辑
当前loggedInUser是全局变量,且getCurrentUser方法逻辑冗余,可以改成在initState里实时获取:
class _ChatScreenState extends State<ChatScreen> { final messageTextController = TextEditingController(); var messageText = ''; User? loggedInUser; @override void initState() { super.initState(); getCurrentUser(); } void getCurrentUser() { loggedInUser = FirebaseAuth.instance.currentUser; } // ... 其他代码 }
修复原理
Firestore要求写入的数据必须是可序列化的基本类型,User对象包含大量不可序列化的属性(比如权限、令牌等),所以只能提取其中需要的字段
相关产品推荐
相关产品推荐

