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

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实例无法直接序列化存储。

另外还有两个衍生问题:

  1. 后续判断isMe时,用User对象和从Firestore读取的字符串对比,永远不会相等,导致气泡样式判断错误
  2. 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对象包含大量不可序列化的属性(比如权限、令牌等),所以只能提取其中需要的字段

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 15:50:25