类型 'Null' 不是类型 'Timestamp' 的子类型 Flutter
Type 'Null' is not a subtype of type 'Timestamp' Flutter
在聊天屏幕中,我希望当地时间与聊天消息一起发布。它正在发布,但我每次 post 聊天时都会收到错误消息。
构建 StreamBuilder> 时抛出了以下 _TypeError(脏,状态:_StreamBuilderBaseState,AsyncSnapshot>>#433c7):
类型 'Null' 不是类型 'Timestamp'
的子类型
如何解决这个错误?
这是聊天屏幕的代码:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import '../../animated_texts.dart'; import '../../constants.dart'; import '../../services/auth.dart';
import 'package:grouped_list/grouped_list.dart'; import 'package:intl/intl.dart';
final FirebaseFirestore _firestore = FirebaseFirestore.instance; late User loggedInUser;
class ChatScreen extends StatefulWidget { const ChatScreen({Key? key}) : super(key: key);
@override _ChatScreenState createState() => _ChatScreenState(); }
class _ChatScreenState extends State<ChatScreen> { final messageTextController = TextEditingController(); late String messageTexts; final FirebaseAuth _authService = FirebaseAuth.instance; final AuthService _authLogOut = AuthService();
@override void initState() {
super.initState();
getCurrentUser(); }
void getCurrentUser() {
try {
final user = _authService.currentUser;
if (user != null) {
loggedInUser = user;
//print(loggedInUser.uid.substring(loggedInUser.uid.length - 5));
}
} catch (e) {
//print('The Error : $e');
} }
@override Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
shadowColor: Colors.transparent,
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.maybePop(context);
}),
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.exit_to_app,
color: Colors.white,
),
onPressed: () {
openDialog();
}),
],
centerTitle: true,
title: const AnimatedTitle(
text: 'AnonC',
fontSize: 25.0,
),
),
body: SafeArea(
child: Container(
color: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const MessagesStream(),
Container(
color: Colors.green,
//blueGrey[900],
padding: const EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
maxLines: null,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
textCapitalization: TextCapitalization.sentences,
autocorrect: true,
enableSuggestions: true,
onChanged: (value) {
messageTexts = value;
},
decoration: kMessageTextFieldDecoration,
),
),
Container(
padding: const EdgeInsets.all(3),
decoration: const BoxDecoration(
color: Colors.white54,
shape: BoxShape.circle,
),
child: TextButton(
onPressed: () {
messageTextController.clear();
_firestore.collection('chatMessages').add({
'messages': messageTexts,
'name': 'Anon - ' +
loggedInUser.uid
.substring(loggedInUser.uid.length - 5),
'userEmoji': 'user emoji',
'timestamp': FieldValue.serverTimestamp(),
});
// messageTexts + loggedInUser;
},
child: Icon(
Icons.send_outlined,
size: 35,
color: Colors.blueGrey.shade900,
),
),
),
],
),
),
],
),
),
),
); }
Future openDialog() => showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.lightGreenAccent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(18.0))),
title: const Text(
"Are You sure you want to Log out?",
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
),
content: const Text(
"Would Love to Welcome You back again!",
style: TextStyle(
color: Colors.black,
fontStyle: FontStyle.italic,
fontSize: 18,
),
),
actions: [
TextButton(
child: const Text("Yes"),
onPressed: () {
_authLogOut.signOut();
Navigator.pop(context, '/');
},
),
TextButton(
child: const Text("No"),
onPressed: () {
Navigator.pop(context);
},
),
],
),
); }
class MessagesStream extends StatelessWidget { const MessagesStream({Key? key}) : super(key: key);
@override Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('chatMessages')
.orderBy('timestamp', descending: true)
.snapshots(),
builder: (context, snapshot) {
List<MessageBubble> messages = [];
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.orange,
),
);
}
final chatMessages = snapshot.data!.docs;
for (var message in chatMessages) {
final messageText = message['messages'];
final messageSender = message['name'];
final currentUser =
loggedInUser.uid.substring(loggedInUser.uid.length - 5);
final timeStamps = message['timestamp'];
if (currentUser == messageSender) {
currentUser;
// //The message is from the logged in user
}
final messageHolder = MessageBubble(
sender: messageSender,
date: DateTime.now().subtract(const Duration(minutes: 1)),
text: messageText,
isME: currentUser == messageSender,
time: timeStamps,
);
messages.add(messageHolder);
}
return Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 1),
child: Container(
padding: const EdgeInsets.all(5),
decoration: const BoxDecoration(
color: Colors.green,
//blueGrey[900],
borderRadius: BorderRadius.only(
topRight: Radius.circular(55),
topLeft: Radius.circular(55),
),
),
// child: ListView(
// physics: const BouncingScrollPhysics(),
// reverse: true,
// // ignore: prefer_const_constructors
// padding: EdgeInsets.all(15),
// children: messageHolders,
// ),
child: GroupedListView<MessageBubble, DateTime>(
padding: const EdgeInsets.all(15),
reverse: true,
//order: GroupedListOrder.DESC,
//padding: const EdgeInsets.only(top: 1),
elements: messages,
groupBy: (messages) => DateTime(
messages.date.year,
messages.date.month,
messages.date.day,
),
groupHeaderBuilder: (messages) => SizedBox(
height: 50,
child: Center(
child: Card(
color: Colors.lightBlueAccent,
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
DateFormat.yMMMd().format(messages.date),
style: const TextStyle(color: Colors.white),
),
),
),
),
),
physics: const BouncingScrollPhysics(),
useStickyGroupSeparators: true,
floatingHeader: true,
itemBuilder: (context, MessageBubble messages) => messages,
),
),
),
);
}); } }
class MessageBubble extends StatelessWidget { final String text; final DateTime date; final bool isME; final String sender; final Timestamp time;
// ignore: use_key_in_widget_constructors const MessageBubble({
required this.text,
required this.date,
required this.isME,
required this.sender,
required this.time, });
@override Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(11.0),
child: Column(
crossAxisAlignment:
isME ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [
Text(sender),
const SizedBox(height: 10),
Material(
color: isME ? Colors.lightGreenAccent[100] : Colors.brown[100],
elevation: 10,
borderRadius: isME
? const BorderRadius.only(
topLeft: Radius.circular(-50),
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
topRight: Radius.circular(10),
)
: const BorderRadius.only(
topRight: Radius.circular(-50),
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
text,
style: TextStyle(
color: isME ? Colors.blueGrey : Colors.black,
fontWeight: FontWeight.normal),
),
),
),
const SizedBox(height: 10),
Text(
DateFormat.jm().format(DateTime.now()),
//DateTime.now().toLocal().toString(),
//DateFormat.jm().format(DateTime.now().toLocal()),
//DateTime.now().subtract(const Duration(minutes: 1)).toString(),
//hour.toString()),
//DateFormat.jm().format(DateTime.now()),
//.format(messages.timeStamp)
),
],
),
); } }
问题是 timeStamps 会在几毫秒内得到一个空值,在那段时间你可以用 currentTime 代替
final currentTime = Timestamp.fromMicrosecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch);
final timeStamps = message['timestamp'] == null ? currentTime : message['timestamp'] as Timestamp;
在聊天屏幕中,我希望当地时间与聊天消息一起发布。它正在发布,但我每次 post 聊天时都会收到错误消息。
构建 StreamBuilder
如何解决这个错误?
这是聊天屏幕的代码:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import '../../animated_texts.dart'; import '../../constants.dart'; import '../../services/auth.dart';
import 'package:grouped_list/grouped_list.dart'; import 'package:intl/intl.dart';
final FirebaseFirestore _firestore = FirebaseFirestore.instance; late User loggedInUser;
class ChatScreen extends StatefulWidget { const ChatScreen({Key? key}) : super(key: key);
@override _ChatScreenState createState() => _ChatScreenState(); }
class _ChatScreenState extends State<ChatScreen> { final messageTextController = TextEditingController(); late String messageTexts; final FirebaseAuth _authService = FirebaseAuth.instance; final AuthService _authLogOut = AuthService();
@override void initState() {
super.initState();
getCurrentUser(); }
void getCurrentUser() {
try {
final user = _authService.currentUser;
if (user != null) {
loggedInUser = user;
//print(loggedInUser.uid.substring(loggedInUser.uid.length - 5));
}
} catch (e) {
//print('The Error : $e');
} }
@override Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
shadowColor: Colors.transparent,
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.maybePop(context);
}),
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.exit_to_app,
color: Colors.white,
),
onPressed: () {
openDialog();
}),
],
centerTitle: true,
title: const AnimatedTitle(
text: 'AnonC',
fontSize: 25.0,
),
),
body: SafeArea(
child: Container(
color: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const MessagesStream(),
Container(
color: Colors.green,
//blueGrey[900],
padding: const EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
maxLines: null,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
textCapitalization: TextCapitalization.sentences,
autocorrect: true,
enableSuggestions: true,
onChanged: (value) {
messageTexts = value;
},
decoration: kMessageTextFieldDecoration,
),
),
Container(
padding: const EdgeInsets.all(3),
decoration: const BoxDecoration(
color: Colors.white54,
shape: BoxShape.circle,
),
child: TextButton(
onPressed: () {
messageTextController.clear();
_firestore.collection('chatMessages').add({
'messages': messageTexts,
'name': 'Anon - ' +
loggedInUser.uid
.substring(loggedInUser.uid.length - 5),
'userEmoji': 'user emoji',
'timestamp': FieldValue.serverTimestamp(),
});
// messageTexts + loggedInUser;
},
child: Icon(
Icons.send_outlined,
size: 35,
color: Colors.blueGrey.shade900,
),
),
),
],
),
),
],
),
),
),
); }
Future openDialog() => showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.lightGreenAccent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(18.0))),
title: const Text(
"Are You sure you want to Log out?",
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
),
content: const Text(
"Would Love to Welcome You back again!",
style: TextStyle(
color: Colors.black,
fontStyle: FontStyle.italic,
fontSize: 18,
),
),
actions: [
TextButton(
child: const Text("Yes"),
onPressed: () {
_authLogOut.signOut();
Navigator.pop(context, '/');
},
),
TextButton(
child: const Text("No"),
onPressed: () {
Navigator.pop(context);
},
),
],
),
); }
class MessagesStream extends StatelessWidget { const MessagesStream({Key? key}) : super(key: key);
@override Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('chatMessages')
.orderBy('timestamp', descending: true)
.snapshots(),
builder: (context, snapshot) {
List<MessageBubble> messages = [];
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.orange,
),
);
}
final chatMessages = snapshot.data!.docs;
for (var message in chatMessages) {
final messageText = message['messages'];
final messageSender = message['name'];
final currentUser =
loggedInUser.uid.substring(loggedInUser.uid.length - 5);
final timeStamps = message['timestamp'];
if (currentUser == messageSender) {
currentUser;
// //The message is from the logged in user
}
final messageHolder = MessageBubble(
sender: messageSender,
date: DateTime.now().subtract(const Duration(minutes: 1)),
text: messageText,
isME: currentUser == messageSender,
time: timeStamps,
);
messages.add(messageHolder);
}
return Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 1),
child: Container(
padding: const EdgeInsets.all(5),
decoration: const BoxDecoration(
color: Colors.green,
//blueGrey[900],
borderRadius: BorderRadius.only(
topRight: Radius.circular(55),
topLeft: Radius.circular(55),
),
),
// child: ListView(
// physics: const BouncingScrollPhysics(),
// reverse: true,
// // ignore: prefer_const_constructors
// padding: EdgeInsets.all(15),
// children: messageHolders,
// ),
child: GroupedListView<MessageBubble, DateTime>(
padding: const EdgeInsets.all(15),
reverse: true,
//order: GroupedListOrder.DESC,
//padding: const EdgeInsets.only(top: 1),
elements: messages,
groupBy: (messages) => DateTime(
messages.date.year,
messages.date.month,
messages.date.day,
),
groupHeaderBuilder: (messages) => SizedBox(
height: 50,
child: Center(
child: Card(
color: Colors.lightBlueAccent,
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
DateFormat.yMMMd().format(messages.date),
style: const TextStyle(color: Colors.white),
),
),
),
),
),
physics: const BouncingScrollPhysics(),
useStickyGroupSeparators: true,
floatingHeader: true,
itemBuilder: (context, MessageBubble messages) => messages,
),
),
),
);
}); } }
class MessageBubble extends StatelessWidget { final String text; final DateTime date; final bool isME; final String sender; final Timestamp time;
// ignore: use_key_in_widget_constructors const MessageBubble({
required this.text,
required this.date,
required this.isME,
required this.sender,
required this.time, });
@override Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(11.0),
child: Column(
crossAxisAlignment:
isME ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [
Text(sender),
const SizedBox(height: 10),
Material(
color: isME ? Colors.lightGreenAccent[100] : Colors.brown[100],
elevation: 10,
borderRadius: isME
? const BorderRadius.only(
topLeft: Radius.circular(-50),
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
topRight: Radius.circular(10),
)
: const BorderRadius.only(
topRight: Radius.circular(-50),
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
text,
style: TextStyle(
color: isME ? Colors.blueGrey : Colors.black,
fontWeight: FontWeight.normal),
),
),
),
const SizedBox(height: 10),
Text(
DateFormat.jm().format(DateTime.now()),
//DateTime.now().toLocal().toString(),
//DateFormat.jm().format(DateTime.now().toLocal()),
//DateTime.now().subtract(const Duration(minutes: 1)).toString(),
//hour.toString()),
//DateFormat.jm().format(DateTime.now()),
//.format(messages.timeStamp)
),
],
),
); } }
问题是 timeStamps 会在几毫秒内得到一个空值,在那段时间你可以用 currentTime 代替
final currentTime = Timestamp.fromMicrosecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch);
final timeStamps = message['timestamp'] == null ? currentTime : message['timestamp'] as Timestamp;