'Messages on flutter chat app not in proper order
I've just completed a basic chat app based on a tutorial. New messages should be shown at bottom but that isn't happening.
When I remove, save and again add reversed in line :final messages = snapshot.data.documents.reversed;
Then it seems to reorder properly, but the very next message is random again.
The only difference is I'm using a more updated firebase plugins.
firebase_core: 0.4.0+8 tutorial was ^0.3.4firebase_auth: 0.14.0+1 tutorial was ^0.8.4+4cloud_firestore: 0.12.9 tutorial was ^0.9.13+1
MessageStream Code :
class MessagesStream extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlue,
),
);
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0),
children: messageBubbles,
),
);
},
);
}
}
Apart from the random order, everything works fine and messages are saved in firebase. Maybe I need to add timecode or something else to make it work.
Any help is appreciated - Thank you.
Solution 1:[1]
After applying this code delete the database in the firebase just as Angela did in her course video.And then restart the app. Your app will work. Also, in the code I have add instruction about those things which you have to add. Add them only. I faced the same problem so i took the help of a coder to resolve this. So it will work in your code too.
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _fireStore = Firestore.instance;
FirebaseUser loggedInUser;
class ChatScreen extends StatefulWidget {
static String chatScreen = 'ChatScreenpage1';
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextEditingController = TextEditingController();
String messageText;
final _auth = FirebaseAuth.instance;
@override
void initState() {
super.initState();
getUserDetail();
}
void getUserDetail() async {
try {
final createdUser = await _auth.currentUser();
if (createdUser != null) {
loggedInUser = createdUser;
}
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
_auth.signOut();
Navigator.pop(context);
}),
],
title: Text('??Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreambuilderClass(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextEditingController,
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
messageTextEditingController.clear();
_fireStore.collection('messages').add({
'sender': loggedInUser.email,
'text': messageText,
'time': FieldValue.serverTimestamp() //add this
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class StreambuilderClass extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _fireStore
.collection('messages')
.orderBy('time', descending: false)//add this
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blueAccent,
),
);
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageTime = message.data['time'] as Timestamp; //add this
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
time: messageTime, //add this
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
children: messageBubbles),
);
});
}
}
class MessageBubble extends StatelessWidget {
final String text;
final String sender;
final bool isMe;
final Timestamp time; // add this
MessageBubble({this.text, this.sender, this.isMe, this.time}); //add the variable in this constructor
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
' $sender ${DateTime.fromMillisecondsSinceEpoch(time.seconds * 1000)}',// add this only if you want to show the time along with the email. If you dont want this then don't add this DateTime thing
style: TextStyle(color: Colors.black54, fontSize: 12),
),
Material(
color: isMe ? Colors.blueAccent : Colors.white,
borderRadius: isMe
? BorderRadius.only(
topLeft: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))
: BorderRadius.only(
topRight: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30)),
elevation: 6,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Text(
text,
style: TextStyle(
fontSize: 20, color: isMe ? Colors.white : Colors.black),
),
),
),
],
),
);
}
}
Solution 2:[2]
Here's the full working solution. Thanks to jquevedo and appbrewery.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedInUser;
class ChatScreen extends StatefulWidget {
static const String id = "chat_screen";
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextContoller = TextEditingController();
final _auth = FirebaseAuth.instance;
String messageText;
@override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
void messagesStream() async {
await for (var snapshot in _firestore.collection("messages").snapshots()) {
for (var message in snapshot.documents) {
print(message.data);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
_auth.signOut();
Navigator.pop(context);
}),
],
title: Text('??Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextContoller,
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
messageTextContoller.clear();
_firestore.collection("messages").add({
"text": messageText,
"sender": loggedInUser.email,
"time": DateTime.now()
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection("messages").snapshots(),
// ignore: missing_return
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.documents;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data["sender"];
final messageTime = message.data["time"];
final currentUsser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
time: messageTime,
isMe: currentUsser == messageSender,
);
messageBubbles.add(messageBubble);
messageBubbles.sort((a , b ) => b.time.compareTo(a.time));
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20),
children: messageBubbles,
),
);
},
);
}
}
class MessageBubble extends StatelessWidget {
final String sender;
final String text;
final Timestamp time;
final bool isMe;
MessageBubble({this.sender, this.text, this.isMe, this.time});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
"$sender ${time.toDate()}",
style: TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
Material(
borderRadius: isMe ? BorderRadius.only(
topLeft: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
) : BorderRadius.only(
topRight: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
elevation: 5.0,
color: isMe ? Colors.lightBlueAccent : Colors.purpleAccent,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 20,
),
child: Text(
text,
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
),
),
],
),
);
}
}
Solution 3:[3]
var notSortedMEssages = snapshot.data.documents;
notSortedMEssages.sort((a,b) => a['time'].compareTo(b['time']);
var reversedSortedMEssages = List.from(notSortedMEssages.reversed);
List<MessageBubble> messageBubbles = [];
Solution 4:[4]
It is showing type 'String' is not a subtype of type 'Timestamp'
Solution 5:[5]
Add the additional two statements in your code to get the ordered result. I have put dots for the same code.
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').orderBy('date').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
....
FlatButton(
onPressed: () {
messageTextController.clear();
_firestore.collection("messages").add({
'sender': loggedInUser.email,
'text': messageText,
'date': DateTime.now().toIso8601String().toString(),
});
},
...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Kit Mateyawa |
| Solution 3 | Dawit Tesfamariam |
| Solution 4 | Abhishek Singh |
| Solution 5 | Hitesh Bhalotia |
